MXC: replace Node bridge with direct wxc-exec.exe spawn#487
Merged
shanselman merged 57 commits intoMay 22, 2026
Conversation
…ssion key When the gateway handshake completes but does not advertise a mainSessionKey (or the legacy mainKey alias), the composer was showing "Connected" with a disabled-but-unlabelled send box. The user had no indication that the gateway needed updating. Changes: - OpenClawChatDataProvider: emit "Incompatible gateway" as ConnectionStatus when HasHandshakeSnapshot=true but MainSessionKey is null/empty and the socket is Connected, instead of plain "Connected". - OpenClawChatRoot: map the "Incompatible" prefix to the new "incompatible-gateway" connState token. - OpenClawComposer: handle "incompatible-gateway" → disable inputs + show Chat_Composer_Placeholder_IncompatibleGateway placeholder. - Resources.resw (all 5 locales): add Chat_Composer_Placeholder_IncompatibleGateway string. - OpenClawChatDataProviderTests: add two focused tests for the incompatible-handshake path (snapshot ConnectionStatus and ComposeTarget.IsReady=false). Closes openclaw#459 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…width Visual cleanup pass on the chat surface (no functional change): - OpenClawComposer: right-edge padding 8 -> 14 in both ThreeRow and InlinePill branches so the action icons (attach / mic / settings / send) no longer jam against the window edge. - OpenClawComposer: dropdowns row ColumnGap 4 -> 6 for clearer separation between Channel / Model / Reasoning pickers. - OpenClawChatTimeline: cap tool-burst cards (CardOf + TaskList listCard) at MaxWidth=720 with HAlign.Left so a single 'exec' row no longer stretches across the full viewport with the Done pill floating at the far right edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Symmetrize user/assistant/tool burst outer margins (16px both sides) - Use bubbleRadius for tool CardOf/listCard and conditional header buttons - Make tool card background Transparent so outline distinguishes it from filled assistant bubble - Drop Plain tool burst footer; assistant follow-up bubble already carries the time Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thing room - Drop the 36x36 spacer that mid-run assistant bubbles inherited; continuation bubbles now sit at the same left inset as tool burst cards above them, so the agent column reads as a single straight edge. - Add 20px top padding above the first message in the scroll content so the conversation does not crowd the window edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ign composer inset - When no assistant avatar shown, drop the leftSlot's right margin so assistant bubbles share the same left edge (16px) as tool burst cards. - Tool burst row header now uses bubblePadding instead of (12,8,12,8) and a 32px MinHeight, so tool rows match chat bubble heights. - Composer outer padding 14->16 to align dropdowns/input flush with chat bubble left edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Assistant footer leftInset now respects per-entry avatar visibility (not the global flag), so continuation entries' timestamps align with the bubble's left edge instead of being indented 44px. - Tool burst button hover/press alphas 0x22/0x33 -> 0x10/0x1C for a subtler reveal that doesn't darken the card on every pointer pass. - Tool card background back to a faint LayerOnAcrylicFillColorDefault tint so the card has gentle presence instead of looking like a pure outline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
User/assistant footer insets now include bubblePadding so timestamps sit flush with the bubble's text content edge instead of the outer bubble border. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When the user avatar is hidden, the rightSlot column still added an 8px left margin even though the column body was empty, leaving an 8px gap on the right of the bubble. Gate the margin on showUserAvatar so the bubble actually reaches the container's right edge when the avatar is off — this makes rightInset (= bubblePadding.Right) place the timestamp flush with the bubble's inner text right edge. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The TaskHeader burst style reserved an avatar slot (36+8px) so the list card lined up with the assistant bubble's text edge, but the Plain and FooterReframe styles started flush at the gutter. When the assistant entry above the burst showed an avatar, the tool cards appeared 44px further left than the bubble. Extracted the avatar-slot wrap into a helper and applied it to all three burst styles so user/assistant/tool share the same left edge regardless of burst style. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Default tool burst rendering switches from Plain (verbose per-row stack) to TaskList:
- While any step is InProgress: auto-expanded, shows 'Working on X...'
- When the burst completes: auto-collapses to a single one-line summary card
('Ran N steps' or last result snippet) with a chevron to expand
- Click chevron to override; per-step rows still individually expandable for
full args/output (3-tier disclosure)
Addresses Scott's feedback: 'I'd like to be able to have tool calls summarized,
or made smaller, or collapsible, so there would be some way to be clear that
work is happening, and if I want to see the verbose logs, I could.'
No data-flow changes — purely the default value of the existing ToolBurstStyle
enum. The dev exploration panel still exposes Plain/TaskHeader/CompactSummary/
FooterReframe/TaskList for tuning.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Tool card aligns under bubble (toolLeftMargin = gutter + avatarSlot + 16) with right edge matched (MaxWidth -= indent). Plain/FooterReframe/CompactSummary/TaskHeader unified. - Footer priority: hide sender/model by default, surface input/output tokens + context % pills. - Preset record defaults updated to match (new presets inherit token/ctx ON, sender/model OFF). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Reorder timeline display within each turn so ToolCall bursts render AFTER the assistant reply (or the thinking indicator if none yet). Gateway still emits tool_start before assistant_delta; only the visual order changes. - Inline 'agent is thinking…' indicator right after the most recent User entry instead of pinning to bottom of timeline, so tool cards visually hang below it. - Tool burst card HAlign Left→Stretch (Plain/FooterReframe/CompactSummary/TaskHeader) so the right edge fills to the bubble's max right boundary instead of shrinking to content. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assistant bubble previously used HAlign=Left with no MaxWidth, so its right edge tracked content width. Tool burst card used HAlign=Stretch with MaxWidth=704, filling further right than the bubble. Give the assistant card MaxWidth=720 and HAlign=Stretch so it always pins to the same max right boundary as the tool card (60 + 720 = 76 + 704 = 780). Tool card now sits indented 16px inside the bubble's left edge with an identical right stroke, reading as a true child of the bubble above. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…p anchored) WinUI's HAlign=Stretch + finite MaxWidth centers the element inside its slot (rather than pinning it left), which detached the bubble from the avatar + timestamp column. Revert to HAlign=Left so the bubble grows from the avatar's edge; MaxWidth=720 still caps the right edge so long messages line up with the tool burst card's right stroke. Short messages keep the previous content-width behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-on-wide-screen) Same WinUI quirk as the assistant bubble: HAlign=Stretch with finite MaxWidth centers the element inside an oversized slot rather than pinning it left. On wide screens the tool burst card drifted away from the bubble's left edge. Revert all four tool burst styles (Plain, FooterReframe, CompactSummary, TaskHeader) to HAlign=Left. Both bubble and tool card now anchor on the left next to the avatar/timestamp column; right edges line up when both fill their MaxWidth (720 / 720-indent). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Star Grid Tool card needs to fill its 704-wide slot so the right stroke aligns with the assistant bubble's right edge, but HAlign=Stretch alone centers the card on wide screens (WinUI's Stretch + finite MaxWidth quirk). Wrap the card in an Auto/Star Grid: the Auto column sizes to the card's MaxWidth (704), keeping the card pinned to toolLeftMargin and filling the slot. The Star column absorbs the rest. Applied to Plain, FooterReframe, CompactSummary, TaskHeader. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assistant bubble is content-sized (HAlign=Left, MaxWidth=720), so we can't predict its rendered width at layout time. Result: the tool card's right edge rarely matched the bubble's right edge, especially with short replies. Solution: thread a per-turn Border[1] slot from RenderAssistantEntry into RenderToolBurst. The assistant bubble's Border drops itself into slot[0] on materialize; the tool card subscribes to bubble.SizeChanged and sets its own Width = bubble.ActualWidth - toolIndent. The two cards' left indent and right edges now stay exactly parallel as the bubble grows, regardless of content length. Reset slot at each User entry boundary so tool cards never bind to a prior turn's bubble. Falls back to MaxWidth/AnchorLeft when no bubble exists (tool-only turn). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Scott feedback: when an agent's reply bubble lands and the tool burst beneath it is fully terminal, fold the N step rows into a single collapsed summary (clickable chevron to expand). While tools are still running, keep showing them as per-step rows so each Running/Done pill stays visible. Added ToolBurstStyle.Auto and made it the process-wide default. Auto resolves per burst at render time: - count == 1 -> Plain (one inline row) - count >= 2 && all terminal -> CompactSummary (1-line + chevron) - count >= 2 && any InProgress -> Plain (live status visible) CompactSummary's existing expand machinery (expandedToolChips HashSet) is reused, so the collapse/expand state persists for the session. Exposed Auto in the exploration panel dropdown for testing the other styles. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two follow-ups from Scott's screenshot: 1. The collapsed CompactSummary header used FlexRow(ColumnGap=6)+padding(12,8,12,8)+MinHeight=22 while BuildRow used Grid[Auto,Auto,Auto,Star,Auto]+margin(6,0,0,0)+bubblePadding+MinHeight=32. Rebuilt the summary header with the exact same Grid template, margins, padding, and MinHeight as the step rows so the chevron / lightning / Task label / Done pill axes line up vertically when the burst is expanded. 2. Removed the trailing FooterCaption(timeStr/TaskFooter()) from CompactSummary, TaskHeader, and FooterReframe returns. The assistant bubble above the tool burst already shows its own timestamp/model/tokens footer, so the time under the tool card was redundant noise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an assistant message is followed by a tool burst in the same turn, defer the timestamp/model/tokens footer so it renders BELOW the tool card(s) instead of between the bubble and the tools. Order becomes: bubble -> tool card(s) -> timestamp/model/tokens Implementation mirrors the existing bubbleSlot pattern: RenderAssistantEntry accepts an Element[1] footerSlot; when supplied, the built footer is handed back to the caller and the inline footer slot in the VStack collapses to Empty(). The outer loop precomputes turn boundaries, hands out a footerSlot whenever a tool entry follows in the same turn, and splices the captured footer Element into timelineRows just after the last entry of the turn (alongside the thinking indicator splice). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This reverts commit 9f84ba9.
When the assistant has produced a reply AND the tool burst would render
as a single visible row (one chip OR a collapsed multi-step summary),
embed the tool card inside the assistant bubble's content area instead
of rendering it as a sibling card below.
bubble {
text...
[tool card] <- nested
}
In-flight multi-step bursts (Plain expanded) and bursts arriving before
the assistant reply still render externally so live progress stays
visible. Plain / TaskHeader / TaskList / FooterReframe styles never
nest — only Auto and CompactSummary opt in.
Implementation:
- RenderAssistantEntry gains an Element? nestedTool param. When set,
the bubble wraps its markdown text in a VStack(8, text, nestedTool)
so the tool card sits flush below the message with an 8px top gap,
inside the bubble's existing padding/border/radius.
- RenderToolBurst gains a bool nested flag. In nested mode CardOf
drops MaxWidth/HAlign.Left and the bubbleSlot Width binding (the
parent bubble already constrains us); a new Wrap helper bypasses
AnchorLeft and the toolLeftMargin/gutter outer margin so the card
stretches inside the bubble.
- Outer loop precomputes turn boundaries, looks ahead from the last
assistant entry of the turn for a contiguous tool burst, and asks
BurstIsNestable to gate the decision (count==1 OR all-terminal under
Auto/CompactSummary). Consumed orderedIdx positions are tracked in a
HashSet so the external render branch emits Empty() for them.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Markdown text has tight line-height with no trailing descender, so an 8px top gap reads as visibly tighter than the 8px bottom padding below the nested tool card. Set the top gap to bubblePadding.Bottom + 4 so the optical spacing matches the gap from the card to the bubble's bottom edge across all PaddingDensity presets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The +16 indent on external tool cards exists so the card visually nests inside the assistant bubble's text column. When the agent is still 'thinking…' and no reply bubble has streamed, that indent makes the tool card hang further right than the thinking indicator above it. When bubbleSlot is null (no assistant entry seen in this turn) drop the indent so the tool card aligns flush under the thinking indicator instead of jutting right. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
High priority fixes: - AutomationName: only set bubble-level name when no nested tool card is present so Narrator can traverse into the nested card. - Status colors: replace hardcoded Done-green / Running-orange / pill white with SystemFillColorSuccessBrush / CautionBrush and TextOnAccentFillColorPrimaryBrush so they adapt to dark/HC themes. - Hover/pressed: 3 button surfaces now use SubtleFillColorTertiary (hover) and SubtleFillColorSecondary (pressed) themed brushes. The tool card uses the lighter Tertiary on hover for a more subtle look (Scott feedback) and to stay visible in dark/HC. - SizeChanged handler leak on the bubble-Width binding fixed: subscribe on Loaded, detach on Unloaded with a stable handler reference so re-renders don't accumulate listeners. Medium priority fixes: - toolCardBgBrush: LayerOnAcrylicFillColorDefaultBrush → CardBackgroundFillColorDefaultBrush (semantic match — the bubble surface is opaque, not acrylic). - Tool card MinWidth = 360 so the 5-column header grid doesn't clip when the assistant reply is short and the bubble shrinks below it. - HC border thickness bumped to 2px via AccessibilitySettings probe (graceful 1px fallback if API throws in unpackaged hosts). - BurstIsNestable now returns false when any tool errored — errors stay external so the failure is visually prominent. Low priority fixes: - (int)bubblePadding.Bottom + 4 → (int)Math.Round(...) for fractional density values. - Remove redundant FontSize = 12 sets after Caption() (Caption already sets 12). Status pills bumped 10 → 11 for readability. - Add uniform-CornerRadius assumption comments at two card sites. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The chat UI styling Kenny iterated on was being persisted to a local preset (%APPDATA%\\OpenClawTray\\chat-exploration-presets.json with IsDefault=true) that overrode the in-memory defaults on startup. New installs and other users were therefore landing on the original code defaults (Mica / Comfortable / Both avatars / 14px / 32px send button) instead of the look reviewed by Scott. Bake the preset values into the actual code defaults so a fresh install matches the design without needing the JSON preset file: BackdropMode Mica -> Acrylic PaddingDensity Comfortable -> Cozy AvatarMode Both -> AgentOnly ComposerIconSize 14 -> 16 SendButtonSize 32 -> 40 Updated in three places to keep them consistent: - ChatExplorationState (the in-memory defaults applied at startup) - ChatExplorationPreset record (defaults when a preset omits a field during deserialization) - ChatVariationPresets.Calm (so users who hit Reset or pick the Calm variation land on the same baseline) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wrap BuildSection in a 2-col Grid with a transparent phantom chevron in col 0 (same glyph + 6px right margin) so the section's lightning glyph and the code block beneath it land at the same x as the header lightning above, regardless of density/font metrics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Composer (OpenClawComposer.cs): - Paste image preview rendered above text in single user bubble - X-button hover changes circular background opacity instead of fading the X glyph (uses opaque SolidBackgroundFillColor* brushes) - Strip stray top/bottom line on TextBox via theme resource overrides (TextControlBorderThemeThickness / focused / pointerOver) - Tighten composer<->actions-row gap (actionsRow margin -8/-4) Reducer (ChatTimelineReducer.cs): - UpsertAssistant reconcile path now scans backward for the most recent Assistant entry, stopping at User boundary, so streams of shape `text -> tool -> tool output -> final text` no longer duplicate the assistant text into a second bubble. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AnchorLeft Grid: single Star col so card measure is bounded by chat viewport - headerRow / summaryHeader: outer [Star, Auto] wrapping inner content + Done pill - Summary Caption: TextWrapping=Wrap + MaxLines=1 + CharacterEllipsis for safe trim - CardOf nested branch: MinWidth=360; external Sync clamps bubble width with Math.Max(360, w) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bubble CornerRadius (16) equals bubblePadding.Right (16) in Cozy preset, so the bubble's corner arc reaches the inner content edge and clips the Done pill at the card's right side. Move the nested card in by a full bubbleRadius (and drop MinWidth=360 which forced the card wider than the bubble in narrow viewports) so the pill is comfortably inside the bubble's rounded shape. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bf876b1 to
50ecb64
Compare
Removes the Node.js + @microsoft/mxc-sdk + tools/mxc/run-command.cjs path for MXC AppContainer sandboxing and replaces it with a pure-C# pipeline that calls wxc-exec.exe directly. node.exe is no longer required at runtime. Key changes: - MxcConfigBuilder: pure function building wxc-exec ContainerConfig from SandboxExecutionRequest + scratch dir. Owns env allow-list + scrub, PATH tool resolution, shell command-line construction, cwd auto-grant. - DirectAppContainerExecutor: ISandboxExecutor that creates per-invocation scratch dir, builds the config, logs a redacted summary (full JSON behind OPENCLAW_MXC_LOG_FULL_CONFIG=1), handles timeout/cancel, and falls back to --config <file> when the base64 config exceeds the cmdline limit. - MxcExecutor (in OrcaCore.Models/Services for cross-project compat): additive caps + RunWithConfigFileAsync. Uses ProcessStartInfo.ArgumentList to avoid manual quoting hazards. WaitForExit() after kill so async stdout /stderr handlers drain before we read. - MxcAvailability: no more RunCommandScriptPath; probes tools/mxc/<arch>/ wxc-exec.exe first, legacy node_modules fallback. - MxcCommandRunner: applies PR openclaw#480 diagnostic logging (LogSandboxRequest/LogSandboxResult) so sandbox settings round-tripping through wxc-exec is verifiable. CWD is logged as <set>/<null>, not the literal path. - csproj: replaces 4 SDK/bridge copy targets with CopyWxcExecToOutput, CopyWxcExecToPublish, ValidateWxcExecShipped, ValidateWxcExecPublished, and a ValidateMxcArchMapping target that errors on unmapped RIDs. - Tests: MxcConfigBuilderTests with 4 SDK-captured golden JSONs (LockedDown/Balanced/Permissive/Custom) for byte-equivalence vs the SDK output, plus env-scrub case-insensitivity, ResolveToolDirsFromPath, cwd auto-grant, timeout defaulting. DirectAppContainerExecutorTests cover the fail-fast paths. Symmetric golden compare with an explicit allow-list for the fields the SDK leaves empty. Security hardening surfaced by adversarial review: - IsDriveRoot guard in ResolveToolDirsFromPath prevents a misconfigured PATH entry from granting the whole system drive as readonly. - CONNECTION_STRING and CONNSTR markers added to credential env-scrub. - Env dict uses OrdinalIgnoreCase so an agent cannot inject a case-variant duplicate of a host-allowlisted var (APPDATA vs appdata). - Reject embedded quotes in --config path; ArgumentList everywhere else. - Host-side CancelAfter mirrors the builder's effective timeout (no unbounded wait when request.TimeoutMs is 0). - ct.ThrowIfCancellationRequested() before the timeout branch so caller cancellation isn't mislabeled as TimedOut. - UTF-8 byte count for the base64 cmdline-overflow threshold. Removed: - src/OpenClaw.Shared/Mxc/OneShotAppContainerExecutor.cs - tools/mxc/run-command.cjs Subsumes openclaw#480. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…pers
The cross-project byte-compat rationale that motivated the OrcaCore.Models /
OrcaCore.Services namespaces was a false premise carried over from the seed
files; no OrcaCore project consumes these types. Cleanups:
- Move MxcConfig.cs + MxcExecutor.cs out of Mxc/Direct/ up one level.
- Rename namespaces to OpenClaw.Shared.Mxc (kills 4 misleading using
directives across the codebase).
- Drop MxcExecutor.ResolveExePath static (dead: path always comes from
MxcAvailability) and tighten the ctor to a required string.
- Drop MxcExecutor.TryCreate (dead: callers ctor + catch directly).
- Drop MxcConfig.Containment default ('appcontainer') and the now-dead
test asserting it. SDK output omits the field; the builder already does.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When MXC sandboxing isn't available on this host (e.g. Windows 10, build < 26100, missing wxc-exec.exe), the agent must still be able to execute commands. Previously the runner denied every system.run with a -1 exit code, stranding users on older Windows. New policy: - Top-level !_isSandboxAvailable() route now falls back to the host runner with a clear [mxc] system.run UNCONTAINED warning instead of returning a deny. - SandboxUnavailableException at runtime (executor reports unavailable on first call) also falls back to host AND invalidates the cached availability so the next call re-probes. - SandboxPage messaging updated: 'Commands run uncontained on this machine' instead of 'commands blocked'. The page still disables its preset controls so users can't tweak settings that don't apply. Tests updated to match: 5 previously-asserting-deny cases now assert host fallback with the expected stdout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
adb94c6 to
f142e25
Compare
…erf optimizations * feat: tool metadata cache, tool call toggle, and stale-closure fix - Cache tool metadata (name, label) to local JSON so tool call types persist across app restarts instead of falling back to 'exec' - Improve ClassifyFlattenedToolOutput heuristic to detect bash, view, grep, git, edit, glob from output text patterns - Add tool call show/hide toggle button to composer toolbar with tooltips on all toolbar buttons - Add CompactSummary as default tool burst style with expand/collapse - Fix stale-closure bug in ChatExplorationState.Changed handlers across Timeline, Composer, ChatRoot, and ExplorationsPanel — event handler captured initial UseState value (0), so Set(1) only triggered one re-render; subsequent toggles were no-ops - Optimize SyncChildren with fast path for empty panels (skip RemoveFromParent O(n) scan) and pre-clear panel on visibility toggle - Enable NavigationCacheMode on ChatPage to avoid full reload on navigation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix: debounce cache writes, atomic file save, closure fixes, and new tests - Debounce SaveToolMetaCache with 500ms timer instead of Task.Run per event - Atomic file write (write .tmp then File.Move) to prevent partial JSON - ChatRoot: read explorationRevRef.Current inside dispatcher lambda - Revert in-place HashSet mutation to proper Set(new HashSet) - Add ToolMetaCacheTests (10 tests): sequential matching, exhaustion, guards - Add classifier edge case tests (9 tests): view, git, exec defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(tray): serialize tool metadata cache writes Move tool metadata timer replacement under the provider lock, version debounced saves so stale timer callbacks cannot overwrite newer snapshots, and serialize unique-temp-file writes to avoid cache corruption under rapid tool events. Add coverage that concurrent metadata additions flush complete valid JSON without leaving temporary files behind. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Christine Yan <christineyan@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Scott Hanselman <scott@hanselman.com>
…Updates Introduces OpenClaw.Shared.AppVersionInfo as the single source of truth for the app version (resolved at runtime from the tray assembly), and replaces ~9 duplicated version lookups across the UI. Rewrites the update-check flow as a two-stage pipeline (metadata gate + install flag) with bounded waits, fresh UpdateInfo writes, and catches for COM/Cancel/InvalidOperation exceptions. Adds a ContentDialog so 'Check for Updates' gives visible feedback when the app is current, when a check fails, or (DEBUG) when skipped. Adds 7 localized Update_* keys across en-us/fr-fr/nl-nl/zh-cn/zh-tw, with a LatinScriptInvariantResourceKeys allow-list so fr/nl keep 'OK' verbatim. Bumps version to 0.4.7. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The BaseEnvAllowList (27 host env vars), DefaultToolNames (12-tool whitelist), IsRequestEnvNameBlocked, and HasCredentialMarker were ported from OpenClaw's own tools/mxc/run-command.cjs — not from @microsoft/mxc-sdk. Keeping them treated that file as a source of truth, which it isn't. Behavior change: the C# builder now mirrors the SDK's getAvailableToolsPolicy() shape (every existing PATH dir is granted readonly, drive roots still skipped per the SDK bug workaround) and no longer copies any host env vars. Agent-supplied request.Env is passed through verbatim, plus TEMP/TMP/TMPDIR forced to scratch. What stays: - cwd auto-grant (AppContainer doesn't auto-grant cwd) - Defensive FilterOutDenied re-pass (deny precedence) - Drive-root guard (documented SDK bug workaround) - TEMP/TMP/TMPDIR -> scratch (sandbox isolation correctness) - Capability mapping (AllowOutbound -> internetClient) - Shell quoting (cmd /S /C, powershell -EncodedCommand) Tests: env-scrub theory and tool-whitelist tests removed (those features no longer exist); new ResolvePathDirsForReadonly tests cover the PATH walking behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…note
Two follow-ups to the previous trim:
(1) Restore env-scrub at the wxc-exec boundary, this time using the
canonical openclaw policy from
openclaw/openclaw:src/infra/host-env-security-policy.json (90
blockedEverywhereKeys + 144 blockedOverrideOnlyKeys + 7 blocked
prefixes). The JSON is copied into this repo at
src/OpenClaw.Shared/Mxc/HostEnvSecurityPolicy.json and embedded
as an assembly resource, mirroring the macOS Swift consumer
(HostEnvSecurityPolicy.generated.swift). New HostEnvSecurityPolicy
class exposes IsBlocked(name); MxcConfigBuilder.BuildEnv now uses
it to filter agent-supplied env.
(2) Sandbox page now discloses the PATH→readonly implicit grant so
users understand which dev tools (git, node, python, dotnet, ...)
are reachable inside the sandbox without explicit configuration.
Small secondary-text note placed inside the Files section, between
the access dropdowns and the Custom Folders box.
Tests:
- HostEnvSecurityPolicyTests covers blocked-key matching, prefix
matching (case-insensitive), malformed-name rejection, benign
pass-through, and a sanity check on policy size.
- MxcConfigBuilderTests gains Build_BlocksDangerousAgentEnv asserting
NODE_OPTIONS/GITHUB_TOKEN/LD_PRELOAD/DYLD_INSERT_LIBRARIES/GIT_SSH_COMMAND
are dropped from the resulting wxc-exec env.
When the upstream JSON changes, re-copy HostEnvSecurityPolicy.json
and rerun tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…walking Adds explicit provenance comments so readers don't have to trace git history to understand why these pieces exist: - HostEnvSecurityPolicy.cs: expanded XML doc spells out the source (openclaw/openclaw:src/infra/host-env-security-policy.json), the macOS analog (HostEnvSanitizer.swift + HostEnvSecurityPolicy.generated.swift), why we enforce at the wxc-exec boundary (defense-in-depth, not gateway centralization), and the threat-model rationale for merging the 'everywhere' and 'override-only' buckets in our agent-env context. - MxcConfigBuilder.BuildEnv: comment cites HostEnvSanitizer.sanitize as the direct macOS counterpart and explains the scrubbing rationale. - MxcConfigBuilder.ResolvePathDirsForReadonly: comment cites the SDK function we mirror (getAvailableToolsPolicy in @microsoft/mxc-sdk:dist/policy.js) plus the drive-root SDK bug workaround. - HostEnvSecurityPolicy.md: new sibling README next to the JSON that describes the update workflow, schema, and how/why we differ from the macOS code-generation approach. No behavior change; comments + docs only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s from App.xaml.cs (openclaw#493) * refactor: remove dead OnRecordingStateChanged handler and its localization strings NodeService.RecordingStateChanged was never subscribed in App.xaml.cs, so the handler and its six Activity_Recording* resource keys were unreachable at runtime. Removing them and the matching entries from all five locale resw files so localization tests remain green. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: extract AppRunMarker from App.xaml.cs The three static run-marker methods (CheckPreviousRun, MarkRunStarted, MarkRunEnded) depended only on a file path. Moving them to a dedicated class removes 37 lines from App and gives the behavior a named home. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: extract CliUninstallHandler from App.xaml.cs The --uninstall CLI path (RunCliUninstallAsync, CliRedact, AttachConsole P/Invoke) had no dependency on App instance state. Moving it to a dedicated static class removes ~150 lines from App and gives the headless uninstall entry point a clear, named home. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: AlexAlves87 <alexalves87@github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…#471) * feat: add ExecApprovalsCoordinator and ICanPresentEvaluator Wires the full two-pass approval pipeline: validate → normalize → buildContext → evaluate(pass1) → prompt/fallback → evaluate(pass2). ICanPresentEvaluator keeps the coordinator UI-free and testable without Win32 APIs. SemaphoreSlim serializes prompt and second pass for concurrent requests. Allowlist persistence and use recording are stubs. Coordinator not wired in production; enforced by test. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: wrap HandleAsync in outer catch to guarantee typed deny on unexpected exceptions Without an outer catch, exceptions from ResolveReadOnly, CanPresent, FallbackDecision, or an out-of-range prompt outcome escaped HandleAsync untyped, breaking the fail-closed contract. Any unhandled exception now returns InternalError("unexpected-exception") with an Error-level log instead of propagating to the caller. Regression test added. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: AlexAlves87 <alexalves87@github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…ilities (openclaw#495) Add 8 tests covering the null-guard, case-insensitive lookup, and return-value contracts of the untested GetLocalNodeCapabilities helper. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Emit redacted human and JSONL setup traces for install, pairing, gateway, repair, and remove flows. Co-authored-by: Mike Harsh <mharsh@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After Step 5a removes the distro-specific VHD directory (e.g. %LOCALAPPDATA%\OpenClawTray\wsl\OpenClawGateway\), the parent wsl\ directory was left as an empty orphan, which also prevented the %LOCALAPPDATA%\OpenClawTray\ directory from being removed by the MSIX uninstaller. Changes: - Add Step 5b in LocalGatewayUninstall.cs: after VHD parent dir cleanup, delete the wsl\ parent directory if it is empty. Non-empty wsl\ dirs (e.g. other distros still present) are preserved safely. - Add WslParentDirAbsent postcondition; include it in AllRequiredPostconditionsMet and AppendPostconditionErrors. - Update validate-wsl-gateway-uninstall.ps1: add wsl_parent_dir path constant, include wsl_parent_dir_absent in postconditions and required-checks list, and capture it in the pre-state snapshot. - Add 3 unit tests (WslParentDirCleanup_*) covering empty→deleted, non-empty→skipped, and already-absent→skipped scenarios. Closes openclaw#467 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DeepLinkParser had no test coverage despite being pure testable logic (no I/O, no Windows dependencies). This adds 27 tests covering: - ParseDeepLink: null/whitespace/wrong-scheme → null - ParseDeepLink: path extraction (with/without trailing slash) - ParseDeepLink: Windows-canonicalized form (slash before query) - ParseDeepLink: single and multi-parameter extraction - ParseDeepLink: case-insensitive parameter lookup - ParseDeepLink: URL-encoded value decoding - ParseDeepLink: empty query → empty Parameters dict - GetQueryParam: null/empty query or empty key → null - GetQueryParam: value retrieval, case-insensitivity, URL decoding Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Issue openclaw#351 was closed but the four undescribed tools were never added to CommandDescriptions in McpToolBridge.cs or skill.md. MCP clients and the local winnode.exe CLI now see the correct parameter shapes and return types for all four tools. Changes: - McpToolBridge.cs: add location.*, device.*, browser.* CommandDescriptions - skill.md: add Location, Device, and Browser control sections with full parameter docs and privacy notes The existing SkillMdDriftTests suite verifies that every command in CommandDescriptions has a matching H3 heading in skill.md — all 115 tests pass with this change. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tizer)
The second env scrub I added at the wxc-exec boundary was redundant.
SystemCapability.HandleRunAsync already runs ExecEnvSanitizer.Sanitize
at the front door for every system.run and rejects the whole command
if any dangerous env var is present — by the time MxcConfigBuilder.BuildEnv
sees the env, it's already been validated. Adding a second filter was
dead code and conflated two separate questions (which list to use, and
where in the pipeline to scrub).
Reverted:
- MxcConfigBuilder.BuildEnv: drops HostEnvSecurityPolicy dependency,
back to agent-env pass-through + structural validity (NUL/CR/LF/'=')
+ TEMP/TMP/TMPDIR scratch override.
- Removed src/OpenClaw.Shared/Mxc/HostEnvSecurityPolicy.{cs,json,md}
(not used anywhere else).
- Removed tests/OpenClaw.Shared.Tests/Mxc/HostEnvSecurityPolicyTests.cs.
- Removed the redundant Build_BlocksDangerousAgentEnv assertion from
MxcConfigBuilderTests.
Net architecture: one env scrub in the chain, in the existing place
(ExecEnvSanitizer at SystemCapability boundary). Whether to upgrade
ExecEnvSanitizer's hand-curated list to the canonical openclaw policy
is a separate question that doesn't need to land in this PR.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The doc comments in MxcConfigBuilder, DirectAppContainerExecutor, ISandboxExecutor, and ShellCommandLine still referenced things that no longer exist after the recent revert: the env scrub at this layer (now in ExecEnvSanitizer at the front door), the JS bridge (deleted), the old tool-name whitelist (replaced with 'walk all PATH dirs'), and the OrcaCore.Services namespace (collapsed into OpenClaw.Shared.Mxc). Rewrites: - MxcConfigBuilder class summary: lists what the builder actually does today (translate SandboxPolicy + request -> MxcConfig, with the four necessary additions: PATH dirs, scratch, cwd auto-grant, deny re-filter). Explicitly notes env scrub happens upstream. - MxcConfigBuilder.Build inline comments: trim and reword to be clear about WHY each addition exists. - ResolvePathDirsForReadonly doc: short version, no SDK-bug archeology. - BuildEnv doc: short version, points at ExecEnvSanitizer for the actual scrub. - DirectAppContainerExecutor: drop OrcaCore.Services and 'previously split with the JS bridge' references. - ShellCommandLine: describe what it does instead of citing the deleted JS implementation. - SandboxExecutionRequest.MaxOutputBytes: drop run-command.cjs mention. No behavior change; doc comments only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ChatPage.xaml: add x:Uid to WaitingPanel title/status TextBlocks and RetryChatButton; reuse existing WebChatErrorTitle / WebChatOpenBrowserButton keys for the error panel controls - InstancesPage.xaml: add x:Uid to BackToConnectionLink TextBlock - Resources.resw (all 5 locales): add ChatPage_WaitingTitle.Text, ChatPage_WaitingStatusText.Text, and InstancesPage_BackToConnectionText.Text Reduces hard-coded XAML string warnings from 286 to 280. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ack) The runner's top-level !_isSandboxAvailable() guard already routes to the host fallback for every call when MXC isn't available, so the ISandboxExecutor passed in for that case is never invoked. The UnavailableSandboxExecutor that always-throws-SandboxUnavailableException was the placeholder injected before the openclaw#494 fix; it's now dead code. - Deleted src/OpenClaw.Shared/Mxc/UnavailableSandboxExecutor.cs. - NodeService.BuildSystemRunRunner: always constructs DirectAppContainerExecutor (one less branch, one less variable). Diagnostic log still reports MXC unavailability when applicable. - MxcCommandRunnerTests.RunAsync_UnavailableExecutor_FallsBackToHost: switched from UnavailableSandboxExecutor to FakeSandboxExecutor { ThrowsUnavailable=true }; same coverage. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Six small cleanups that together drop ~95 lines of dead code, dead fields, dead comments, and one-use helper types. No behavior change except the diagnostic log no longer carries a redundant securityLevel preset label. 1. MxcCommandRunner: delete DetectPreset + MatchesPreset (~50 lines). They existed only to put a LockedDown|Balanced|Permissive|Custom label on the diagnostic log, hardcoding preset thresholds that had to track SandboxPage. The log already emits all the underlying settings; the label was redundant. 2. ShellCommandLine class in MxcConfigBuilder.cs: changed from public to internal -- only MxcConfigBuilder.Build calls it. 3. MxcAppContainer.Name field: deleted. Deprecated in the SDK since 0.4.0-alpha and never set on our side. 4. MxcFilesystem.ExecutablePath field: deleted. Not in any SDK schema and never set on our side. 5. Stripped Additive (OpenClaw) comments throughout MxcConfig.cs. They tagged fields as "additive vs the original OrcaCore seed file"; since we deleted the OrcaCore claim the distinction is gone. 6. MxcArchHelper: inlined as a private static method of MxcAvailability. Tests: dropped the securityLevel:"Custom" assertion in RunAsync_LogsSandboxSettingsSnapshotAndPolicy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the Node-based MXC command bridge with direct wxc-exec.exe invocation and keep local Windows sandbox behavior reliable. Adds least-privilege cwd read grants, the temporary readonly drive-root shell startup workaround, NTFS/ReFS diagnostics, local MXC test gating, and GitHub Actions self-skip for MXC integration tests. Fixes openclaw#494 Co-authored-by: Barbara Kudiess <7658216+bkudiess@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the Node.js bridge for MXC AppContainer sandboxing with a direct C#
wxc-exec.exepipeline.node.exeandtools/mxc/run-command.cjsare no longer required at runtime.This keeps the original #487 direction and adds maintainer follow-ups from local MXC debugging: cwd grants, shell startup compatibility, ReFS diagnostics, local test gating, and Windows 10 fallback behavior.
Fixes #494
What changed
MxcConfigBuilder,DirectAppContainerExecutor, andMxcExecutorbuild and launchwxc-exec.execonfigs from C#.wxc-exec.exefrom@microsoft/mxc-sdkintoOpenClaw.Tray.WinUI\bin\...\tools\mxc\<arch>\and validates it is present.cmd.execurrently stats the drive root during startup.system.runfalls back to host execution with warning UI instead of blocking all commands.Validation
./build.ps1dotnet test ./tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj --no-restore— 1898 passed / 29 skippeddotnet test ./tests/OpenClaw.Tray.Tests/OpenClaw.Tray.Tests.csproj --no-restore— 1191 passedGITHUB_ACTIONS=trueandOPENCLAW_RUN_INTEGRATION=1