* fix(status): carry prepared thinking catalog
Resolve status thinking levels from model metadata prepared by each caller.
Model-state paths carry their resolved catalog forward, while standalone status
surfaces make one side-effect-free read without provider discovery.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
* test(status): assert read-only native catalog load
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(status): consolidate live model switches so /status stops reporting stale state
After /model during an active run, liveModelSwitchPending could survive
forever on CLI-harness sessions (they never pass the embedded
attempt-recovery clear), and /status gave no hint that a live switch had
not been applied yet — the second symptom of #97517.
Add a post-run consolidation in the generic reply runner: once a
completed non-heartbeat run has executed the persisted selection, the
pending flag is spent and cleared. While a switch is genuinely pending,
/status now annotates the Model line with 'live switch pending' instead
of implying the new selection is already running.
* refactor(status): make live-switch consolidation atomic and default-aware
Review findings: compare-and-clear now happens inside one patchSessionEntry
callback against the same row snapshot, so a concurrent /model that persists
a newer selection can never be consumed by the finished run's result. The
selection also resolves from module-level defaults (matching the embedded
attempt-recovery caller) instead of the finished run's provider, which
misclassified a completed /model default switch.
* fix(status): resolve the owning agent before consolidating a default switch
Without an explicit agent id, /model default consolidation compared the
ran model against library-wide defaults; when config overrides the
primary, the pending flag never cleared. Derive the canonical agent id
from the session key so the agent's configured default is used.
* fix(status): resolve the owning agent before selecting the session store
Agent-scoped store templates need the derived agent id for store-path
resolution too, or the consolidation patches a different store than the
one holding the pending flag.
* chore(ci): retrigger after GitHub Actions startup failures
* fix(codex): full-auto approvals survive relay unavailability, typed failure classification, usage line after fallback
- full-auto (approvalPolicy never + danger-full-access) exec approvals no
longer get declined when the native hook relay is unreachable BEFORE
invocation; an invoked relay's explicit deny, malformed output, or nonzero
exit still fails closed (#107447)
- startup timeout/abort and request-timeout classification use typed error
discriminants instead of message prose; EPIPE detection walks error causes;
compaction keeps the message-based thread-not-found gate because codex-rs
exposes no dedicated code (its own contract test asserts the message) and
the generic -32600 would over-match unrelated invalid requests (#99270)
- /status keeps the Codex usage/quota line for sessions whose persisted
agentHarnessId is codex even when the effective runtime fell back to
OpenClaw Default; never-codex sessions remain excluded (#105184)
* chore(codex): keep startup error reason type module-local
* fix(agents): reject synchronous sessions_send self-targets, break down missing-cost entries, dedup usage merges
- sessions_send fails fast with a clear error when a synchronous send resolves
to the calling session's own key, instead of enqueueing behind the sender's
own lane until timeout and falling back to an empty reply; fire-and-forget
(timeoutSeconds: 0) self-delivery keeps working (#107172)
- missingCostEntries now carries a provider/model breakdown surfaced on the
status runtime line and gateway CLI cost line, so zero-priced usage (e.g.
openai-codex/*) is attributable instead of an opaque counter (#98348)
- codex-synthetic-usage merge helpers share one precedence helper; behavior
unchanged, duplicate ranking logic deleted (#107497)
* test: use a valid billing type in synthetic-usage precedence case
* test: fix billing expectation to match valid type
New grouped sessions tool (patch: label/pin/archive/model/thinking; group
CRUD; owner-gated; no reset/delete/compact). sessions_spawn gains visible +
worktree via sessions.create with admission reservation and abort-confirmed
rollback; a started run with no run id is aborted and deleted rather than
left orphaned. subagents tool reads the unified task_runs ledger tree-scoped
(subagent/acp/media/cron) and cancels detached runs, enforcing the
controlScope gate for leaf callers. session_status gains a cost line.
Agent-origin model patches record a fallback marker; runs failing on
auth/billing/model_not_found revert to the last working model (transient
errors never revert), and an independent thinkingLevel change realigns the
marker so revert cannot clobber it.
Refs #107237
* refactor(deadcode): trim auto-reply and CLI exports
* refactor(deadcode): trim cron and task exports
* refactor(deadcode): trim fleet and process exports
* test(deadcode): exercise live task and process seams
* test(fleet): cover stream redaction through owner module
* refactor(security): trim dead internal exports
* refactor(secrets): trim dead internal exports
* refactor(deadcode): trim remaining src exports
* refactor(deadcode): remove test-only runtime exports
* refactor(deadcode): trim pairing test exports
* refactor(deadcode): reconcile refreshed baseline
* test(auto-reply): deduplicate queue state imports
* fix(core): make indexed access explicit in auto-reply, infra, and config
Part 1/3 of the src NUIA phase-3b burn-down (#104600): iteration and
destructuring over index reads, boundary guards on parsed input, and
named invariants. Config path walkers bind the path head once; SQLite
migration key handling is hoisted without query-shape changes.
* fix(core): make indexed access explicit in cli, gateway, commands, security, shared
Part 2/3: argv/token selection restructured, gateway event/attachment
invariants named, security parsers stay fail-closed (invariant
violations throw), edit-distance matrices access checked entries.
* fix(core): make indexed access explicit across remaining src surfaces
Part 3/3: channels, plugins, process, cron, plugin-sdk, media, logging,
tui, hooks, daemon, and small directories. Latent bug fixed: a tailnet
resolver could leak undefined through a string|null contract and now
fails with a descriptive local error.
* fix(core): keep optional boundaries optional after per-commit review
Review findings: expectDefined misused where absence is a legitimate
state. CLI --profile/route-args missing next tokens take their existing
miss paths; help normalization compares --help against the last
positional again; first-time plugin install spreads absent cfg.plugins;
denylist scan iterates manifest dependency entries instead of throwing
on omitted sections; tailnet resolver returns a guaranteed string at
the source instead of a caller-side undefined throw.
* refactor(core): closed-key provider labels and honest optional passthroughs
PROVIDER_LABELS becomes a satisfies-typed closed record (static reads
provably defined; dynamic lookups go through providerUsageLabel with
honest string|undefined). Status-scan overview passes its optional
params through unchanged instead of asserting them.
* fix(channels): make getChatChannelMeta honestly optional
The original signature claimed ChatChannelMeta while leaking undefined
on bundled channel id metadata drift; three of four callers already
handled absence. The return type now says so, and the one assuming
caller falls back to the raw channel label.
* fix(core): index-safety for post-rebase main drift
Covers the sqlite-sessions flip and auth-source-plan code that landed
mid-phase, plus the channel-validation test consuming the now honestly
optional getChatChannelMeta.
* refactor(channels): split chat-meta accessors along the SDK contract
getChatChannelMeta keeps its shipped plugin-SDK signature (defined for
bundled ids, fail-loud on impossible misses); new findChatChannelMeta
carries the drift-tolerant optional contract for core auto-enable and
formatting paths.
* fix(qa-channel): own channel metadata instead of a guaranteed-undefined catalog lookup
qa-channel spread getChatChannelMeta over an id that is never in the
bundled catalog, shipping an empty setup meta by accident; the fail-loud
SDK accessor exposed it. The channel now declares its metadata once.
* fix(gateway): heartbeat projection lookahead is optional at the transcript tail
expectDefined wrapped messages[i + 1] whose absence on the final message
is the normal case; the adjacent ternary already handled it. Restores
the plain optional read with an explicit guard in the pair condition.
* fix(plugin-sdk): channel plugin factory tolerates non-bundled channel ids again
createChannelPluginBase spreads bundled catalog meta for ANY channel id,
where absence is the normal case for external plugins; the resolver is
honestly optional again while the exported bundled-id accessor keeps the
fail-loud contract.
* fix(core): spreads of optional config sections stay optional
Fresh-setup and first-install paths (crestodian setup inference, hook
installs, agent config base, target agent models) legitimately lack the
section being rebuilt; spreading undefined is the shipped {} semantics.
Removes the remaining gratuitous assertion wraps found by tree audit.
* @
fix(status): surface auto-fallback model in status and session_status (#96126)
When a session falls back to an alternate model via auto-fallback
(modelOverrideSource: "auto"), both `openclaw status` and `session_status`
silently showed the active fallback model without indicating it differs
from the configured primary. The mismatch gate used
hasUserPinnedModelSelection() which returns false for auto-fallback.
- status.summary.ts: widen mismatch gate from hasUserPinnedModelSelection
to entry?.modelOverride != null; emit distinct "fallback selected"
reason alongside existing "session override"
- status.command-sections.ts: add fallback-specific wording ("auto
fallback" / "check provider availability") while keeping the
modelSelectionReason filter intact (no false-positive null-reason rows)
- status-message.ts: add sessionHasAutoFallback detection for the
session_status RPC path; show "auto fallback" / "check provider" label
instead of "pinned session" / "clear /model default"
Co-Authored-By: Claude <noreply@anthropic.com>
@
* fix(status): narrow fallback detection to real fallback provenance
Replace entry?.modelOverride != null with
hasSessionAutoModelFallbackProvenance(entry) to avoid mislabeling
configured subagent automatic model selections as provider fallback.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(status): distinguish configured models from fallback
Co-authored-by: LZY3538 <liu.zhenye@xydigit.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(status): distinguish runtime-loaded plugins from installed inventory
/status plugins merged disk-scan plugin records with the active runtime
registry, so the detailed Loaded: list could include plugins that were
installed/config-enabled but never loaded at runtime. Record the runtime-
loaded plugin ids on the health snapshot, carry them through the merge, and
make Loaded: reflect runtime-confirmed plugins; show installed/discovered
plugins that are not active as a neutral "Installed (not active)" inventory
line rather than an error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(status): include pinned runtime registries in loaded ids
Build runtimeLoadedPluginIds from all live runtime registry surfaces via
collectLivePluginRegistries() (active plus any pinned channel / http-route /
session-extension registry) and render Loaded: from that id set directly, so a
plugin live only through a pinned surface still counts as loaded instead of
being dropped or misreported as "Installed (not active)".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Add optional directUserId field to ChannelModelOverrideParams so the
shared channels.modelByChannel resolver can match DM-specific config
entries. Callers pass sessionEntry.origin?.nativeDirectUserId.
Closes#53638
Co-authored-by: Thomas Zhengtao <thomas.zhengtao@gmail.com>
Show elapsed session duration in the status footer using the canonical session lifecycle timestamps and compact formatter.
Fixes#68226.
Co-authored-by: Alix-007 <li.long15@xydigit.com>