* fix(workboard): record resolved runtime metadata instead of hardcoded codex engine
Workboard executions labeled every dispatched run engine=codex, model=default,
and id suffix :codex even for Claude/other harness agents. The gateway agent
admission phase now returns the resolved {harness, provider, model} for plugin
subagent runs; the dispatcher records it verbatim and omits engine/model when
unresolved. Engine becomes an open runtime identifier in the workboard
contract (built-in launch choices stay a closed list), store/UI normalizers
preserve historical labels as written instead of inventing codex, and new
execution ids use an :agent-session suffix. Fixes#108362
* fix(workboard): accept undefined engine in ui engineModel helper
* fix(ui): honor timezone in usage detail timeline
Apply the selected local or UTC calendar boundaries to session detail points and keep the full final day.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
* test(ui): make usage timezone regression deterministic
Model a non-UTC local calendar through Date getters instead of relying on runtime TZ mutation.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
* test(ui): type local date getter mocks
Annotate Date receivers so the deterministic timezone regression passes strict test type checking.
Co-Authored-By: OpenAI Codex <noreply@openai.com>
* fix(ui): complete usage timeline timezone handling
---------
Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor(ui): share one panel tab strip between terminal and browser
Extract the terminal tab strip (tab + attached close button as one
surface, from #108611) into a shared panel-tab-strip module and migrate
the in-app browser panel onto it. The browser tabs inherit the attached
close design; terminal tabs gain middle-click close and long-label
ellipsis; both get the empty-group workaround and a strip that shrinks
inside the flex header. Net -24 prod LOC; duplicated tp-/bp- strip CSS
deleted.
* fix(ui): keep shared panel new-tab button stable
The flex height constraint chain was broken at three points in the
board-mode layout, preventing both the proposal queue and detail
body from scrolling:
1. .sw-hub-panel > .skill-workshop used a > child selector but
.skill-workshop is nested inside an inner wa-tab-panel wrapper,
so the rule never matched. Switched to a descendant selector.
2. #skill-workshop-mode-panel (the inner tab panel) had no flex
styles, defaulting to display:block which cannot pass through
flex height constraints. Added explicit flex container styles.
3. The wa-tab-panel shadow-DOM part(base) wrapper also needs flex
overrides to fill the host height. Added part selectors with
display:flex, flex:1, min-height:0, and width:100% to preserve
full-width block behavior for non-flex children like the
history scan banner.
Also added flex-shrink:0 to .sw-history so the banner does not
collapse when .skill-workshop receives a definite height.
Fixes#108196
The web terminal's per-tab close control rendered as a detached floating
square next to the tab. It now joins the tab as one surface: shared hover
background, active accent underline continuing beneath it, full tab
height, with its own rounded inner highlight on hover.
The live codex text provider was a redundant projection of the openai
catalog (exclusive provider ownership; the openai plugin's ChatGPT OAuth
discovery already serves gpt-5.6-* route-aware). Folding it:
- extensions/codex no longer registers a text provider, catalog entry, or
synthetic text auth; provider.ts/provider-catalog.ts/provider-discovery.ts
and the route-blind model-name heuristics are deleted; the narrow
post-harness reasoning fallback moves to an app-server-owned module
- openai thinking policy keys on explicit selected-route provenance
(api === openai-chatgpt-responses) instead of value-shape inference
- models.list gains an optional additive agentRuntime field (configured
intent); session agentHarnessId remains the execution proof
- doctor --fix migrates the shipped codex/* config shape end to end:
every model slot, provider-config merge with blocker-aware conflict
handling, sessions, cron payloads (two-phase: runtime policy persists
before cron refs rewrite), transcripts; migrated refs carry model-scoped
agentRuntime.id=codex preserving the shipped wizard semantics; auto
runtime policies normalize to codex with sibling fields preserved;
blocked provider conflicts retain the whole legacy namespace fail-closed
with an actionable warning
- the stale openai:default profile cleanup (#91352) was deliberately
deferred to a follow-up after review showed it needs per-agent identity
proofs; doctor keeps warning about unusable profiles
Fixes#105561Fixes#84637Fixes#90420
Final review findings:
- Wrap the operator-approval copy/drop/rename in a single immediate
transaction so a crash mid-migration cannot strand or lose rows.
- The inference fallback marked a provider attempted before probing, so a
stale requester credential blocked another agent's valid same-provider
route. Dedup by credential owner and only retire a whole provider on
provider-wide failures (auth/billing may differ per credential owner).
Refs #107237
New openclaw delegation tool relays to openclaw.chat in-process; persistent
writes surface through the existing durable operator-approval registry as a
third system-agent kind (no parallel store), armed only by a human operator
in the Control UI — a delegated agent can never self-approve. Setup wizards
(channel/model/open-setup/open-tui) are refused in delegated mode so a
machine agent cannot complete setup or persist credentials unattended.
Vendor-neutral inference fallback ladder (requester route first, then other
authed providers by provider id). update.run removed from the gateway tool.
Caveman system-prompt fragment steers config/channels/plugins/agents/updates
to the openclaw tool. Doctor-owned state migration widens the approval-kind
constraint; runtime stays canonical-only.
Refs #107237
* fix(ui): show <1m for sub-minute quota reset windows instead of 0m
formatQuotaReset computed Math.floor(diffMs / 60_000) for the minute branch.
When a provider quota window resets in under 60 seconds (common for 5h Anthropic
or 3h Codex windows near rollover), the floor is 0 and the UI rendered
"Resets in 0m" — nonsensical and indistinguishable from "already reset".
Add a sub-minute guard returning "<1m", matching the convention already used
by formatRemainingShort and formatRelativeTimestamp elsewhere in the codebase.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(ui): cover quota reset display boundary
Co-Authored-By: ZCode <noreply@zcode.ai>
* test(ui): cover elapsed quota reset boundary
Co-Authored-By: ZCode <noreply@zcode.ai>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: ZCode <noreply@zcode.ai>
* feat(ui): add follow-up behavior setting (queue vs steer) for active runs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* default follow-up behavior to steer
Follow-ups sent while a run is active now steer into the running turn by
default; queue mode becomes the persisted opt-out. Offline/disconnected
sends still always queue.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(ui): steer active follow-ups by default
---------
Co-authored-by: openclaw-clawsweeper[bot] <openclaw-clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>