The cloud machine picker described each class in prose ("Cheap smoke checks
and small repos"), which was the widest thing in the row, got ellipsized, and
did not say what the operator actually picks on. Machine options now carry the
class shape and the picker renders "32 vCPU · 64 GB" instead.
Crabbox reports per-provider class shapes from `providers --json`; the plugin
reads that catalog once per lifecycle and attaches cpu/memoryGb to each option.
A missing binary, failed command, unparseable output, absent classes, or an
unmatched provider all degrade to label-only rows, so an older Crabbox keeps
working.
`description` was never released, so it is removed rather than deprecated, and
listMachineOptions becomes async for the catalog read. Deletes the duplicate
machine-option projector in the environments server method.
The Control UI settings page (#124864) and the New Session place
picker's machine section had no doc coverage — the configuration and
dispatch walkthroughs only described the JSON/RPC paths.
Center sidebar session pin and menu actions across the full two-line row while reserving text width on hover and touch. Add browser geometry regression coverage.
Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
* fix(copilot): add OpenClaw prompt guidance
Copilot append-mode system messages included credential safety, workspace bootstrap, and extra context but omitted OpenClaw delegation and reply-delivery policy.
Build guidance from the final policy-filtered tool surface so visible delegated work, Skill Workshop, and source replies follow the same behavior as Codex.
* fix(copilot): break prompt guidance import cycle
The CI architecture gate detected a cycle through attempt-config and prompt-guidance. Isolate raw-run mode detection in a leaf module.
* fix(gateway): apply configured edge auth to session-URL targets
Follow-up to #125700.
A URL target with explicit auth skipped config loading and silently dropped
gateway.remote.edgeAuth. Config I/O is now skipped only for plaintext loopback
targets.
Also document mandatory passEnv: ["HOME"] for the cloudflared exec provider.
* fix(gateway): keep explicit secure connections working when config is invalid
Addresses the ClawSweeper finding on #125982 by preserving the invalid-config recovery path while still applying configured edge auth.
* fix(workboard): make ownership updates atomic
Persist launch intent before worker acceptance, reconcile accepted identities,
fence cross-host card mutations, and deduplicate session capture in SQLite.
Closes#125932
* test(workboard): keep race fixtures plugin-local
Use a plugin-local synchronization helper and update the sessions-page capture
expectation for the new store-owned RPC.
* fix(workboard): fence lifecycle and restore races
Apply terminal lifecycle state only while the matched association is current,
and converge concurrent archived session restores on the active winner.
* feat(ui): collapse automation sessions in activity feed
* feat(ui): show live run status on activity feed rows
* feat(ui): make run inspector deep-link only
* fix(ui): only link inspect run to a current active run
Addresses ClawSweeper P1: a stale observer digest could deep-link a live
row to an ended run's audit record; trust digest runId only when it is
still in activeRunIds.
runConfigUnset scoped its missing-path error to --dry-run, so the real
invocation printed "No change" and exited 0 while --dry-run on the same
input exited 1 with a specific message. Dry-run exists to predict the
real run, and a typo'd unset in a script reported success.
Restore the failure for both modes, keeping the strict-config assertion
introduced alongside the regression and the JSON5 byte preservation it
was added to guarantee.
* fix(ui): never restore a stored draft over typed new-session composer text
After a reload the new-session composer renders before the gateway
recovery scope arrives; selectRoute then zeroes the draft revision, so
text typed in that window lost to the stored draft's higher revision and
the async restore overwrote (or, interleaved with input, appended to)
what the user had typed. Seen on PR #125690 CI (checks-ui-e2e 5/12, run
32122284238) as a doubled composer value.
NewSessionDraftPersistence now records a pristine baseline at each
programmatic content replacement (page reset, navigation handoff,
restore apply) and restores only when no user mutation happened past it;
otherwise the typed text wins and persists above the stored revision,
including minting a revision for text typed before route activation so
that draft still lands. The chat composer sibling is unaffected: its
revision lineage persists synchronously to localStorage.
* test(ui): run draft-persistence regression in the isolated ui lane
The regression test mocks the statically imported
durable-composer-persistence helper (the store runtime is only
dynamically imported, so under the shared isolate:false module graph its
mock alone is packing-dependent). Isolating the file makes the mocks
deterministic and keeps the shared-lane file set unchanged so sibling
packing does not shift.
Recheck the request signal after an async response hook settles so a concurrent abort cannot reach response body handling. Related to #125802 and #125834.
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
Keep drain-owned recovery state authoritative when the pre-restart run reaches a normal terminal event, so startup continues the original request without requiring a manual retry.\n\nRelated: #57425
`openclaw approvals allowlist add|remove` printed "Writing local approvals."
from the shared target-resolution helper, before the mutation decision was
made. Both idempotent paths ("Already allowlisted.", "Pattern not found.")
returned without saving, so the CLI announced a write that never happened.
`approvals set` had the same problem: it announced the write and then rejected
unparseable input.
Move the announcement from `loadWritableSnapshotTarget` into the local branch
of `saveSnapshotTargeted`, the function that owns the write. Every caller of
the shared seam is fixed at once and exit codes are unchanged: idempotent add
and remove still leave the requested end state satisfied and exit 0.