Stabilizes model auth marker tests against the current manifest-metadata discovery seam and isolated Vitest environment.
Runtime behavior is unchanged; provider-owned non-secret markers remain declared in plugin manifests.
The usage-bar subdirectory was not covered by any full-suite shard glob
(template.test.ts is disqualified from unit-fast by filesystem-state),
failing the shard coverage check. Extend autoReplyCoreTestInclude to the
subdir; unit-fast-eligible files are auto-excluded by the scoped config.
Also create the temp dir explicitly in the missing-file test instead of
binding an unused tmpFile result, which tripped oxlint no-unused-vars.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
loadUsageBarTemplate ran a synchronous statSync on every reply (and readFileSync
on mtime change) to check template freshness — synchronous filesystem I/O in the
latency-sensitive reply path, which on a slow / networked / blocked filesystem
can stall /usage full delivery (and the single-threaded event loop with it).
Read the template once into memory and keep it fresh with a filesystem watcher
(persistent: false), so the per-reply path is filesystem-free. A missing file is
not cached (a later-created template is still picked up on a subsequent call); a
watch failure leaves the one-time load in place. Adds template.test.ts covering
inline / file / invalid-JSON / missing-then-created and the FS-free hot path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Consume the usageState contract's contextUsedTokens (populated by #89629) so
the context gauge reflects real end-of-turn window occupancy instead of the
multi-call turn aggregate, which overstates it (often past 100%) and pins the
meter full while /status shows the true figure. Fall back to the aggregate
when contextUsedTokens is absent (single-call turns, where they coincide).
Also expose the final model call's usage as usage.last.* (input/output/cache +
cache_hit_pct) so a template can render the last exchange vs the turn
aggregate.
Adds the consumed fields (contextUsedTokens, lastUsage) to
PluginHookReplyUsageState as the renderer's type dependency; their population
lands in #89629.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
num truncates sub-1000 values to integers, so monetary fields like
cost.turn_usd rendered as 0 (or with full float noise when piped raw).
Add a fixed:N verb that formats a number to N decimal places (default 2),
returning empty string for non-numeric input — matching the other
formatters' guard style.
{cost.turn_usd|fixed:4} 0.03771985 -> 0.0377
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- no-misused-spread: use Array.from for code-point glyph split (same
behavior, astral glyphs intact) instead of string spread.
- no-base-to-string: return the case glyph only when it is a string.
- no-unnecessary-type-assertion: drop redundant cast already narrowed
by isObject.
No behavior change (render output byte-identical; tsgo clean).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Formatting (import order, indentation) and bracket-notation access for
reserved template keys (_aliases/_default) to satisfy no-underscore-dangle.
No behavior change.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Render the per-reply /usage full footer in core from a declarative template
(the openclaw.usageBar.v1 format) when messages.usageTemplate is set; fall back
to the built-in line otherwise. Ports the reference usage_bar.py engine to TS so
no external process is involved (the external surface is just template data).
- usage-bar/translator.ts: engine (verbs num/dur/pct/inv/alias/meter, segment
forms text/when/map/each, output.surfaces, item_scales). Codepoint-correct
glyph indexing; fail-open (empty render -> boring fallback).
- usage-bar/contract.ts: buildUsageContract (snapshot -> openclaw.usageLine.v1).
- usage-bar/template.ts: resolve from a path (mtime-cached) or inline object.
- agent-runner: capture the per-turn usage snapshot and render the template at
the /usage full branch in place of the built-in line when configured.
- messages.usageTemplate config (string path | inline object) + strict schema.
- translator.test.ts: verb parity, segment forms, astral glyphs, e2e render.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes#90685 by allowing models.list availability to use matching auth-profile credentials when provider config contains a non-env SecretRef, while preserving unavailable results for unresolved SecretRef-only providers.
Adds isolated regression coverage for file SecretRefs and secretref-managed provider markers.
Co-authored-by: Rohit <rohitjavvadi2@gmail.com>
Bounds default model browsing to configured/read-only discovery while preserving explicit full-catalog browsing. Reuses prepared plugin metadata and auth state without triggering external CLI discovery on the picker hot path, while retaining provider normalization and canonical runtime aliases.
Verified with focused model tests, official OpenAI and Anthropic transport suites, fresh live tool calls for both providers, a full build, AWS check:changed, remote Docker OpenAI tools E2E, and green PR CI.
Fixes#91809.
Co-authored-by: samson1357924 <98934496+samson1357924@users.noreply.github.com>
A missing per-turn authMode was mapped to "oauth", so an OpenAI api-key turn
that arrived without an explicit auth mechanism could resolve and display
ChatGPT subscription windows that aren't its own — served straight from the 60s
limits cache, which the "re-checked at fetch time" guard does not cover.
Treat a genuinely absent signal as non-eligible (same as api-key): no usage
provider resolves and the footer omits limit windows. Present mechanisms are
unchanged — oauth/auth-profile/token stay eligible, and only OpenAI is gated on
the credential type so other providers are unaffected. A real oauth/profile turn
always carries its mechanism; one arriving blank is an upstream tagging bug to
fix at the source.
Inverts the now-incorrect "absent => oauth-eligible" test into regression
coverage for the absent/api-key case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
context.used_tokens / pct_used were derived from the snapshot's aggregate
prompt total (cacheRead+cacheWrite+input). Over a multi-call tool-loop turn
that is the run AGGREGATE, overstating window occupancy (often past 100%) so a
footer's context gauge pins full while /status shows the true ~7%.
Add two optional fields to PluginHookReplyUsageState and populate them in the
reply path:
- contextUsedTokens: the final call's prompt size (agentMeta.promptTokens) =
real end-of-turn occupancy, a point-in-time state, not the aggregate.
- lastUsage: the final model call's usage only (vs `usage`, the turn
aggregate), so a footer can render the last exchange's i/o + cache.
Both optional and additive; consumers fall back to the aggregate when absent
(correct for single-call turns). Renderer consumption lands separately (#89835).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
requestShaping.authMode is the auth *mechanism* (e.g. "auth-profile" for a
configured auth profile), not the credential *type* resolveUsageProviderId
expects. Gating limits on it === "oauth"/"token" dropped 📊 for legit OAuth
(profile-based) turns. Map it: api-key/aws-sdk -> no usage provider (cannot
borrow cached oauth windows); oauth/token/auth-profile/absent -> usage-eligible,
with the real credential re-checked at fetch time.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses review feedback on #89629.
1) Provider-limit resolution no longer defaults to OAuth. getProviderUsageLimits
and getProviderUsageLimitsCached resolved with `credentialType ?? "oauth"`, and
the agent-runner snapshot call passed no credential type, so an api-key OpenAI
turn could borrow cached OAuth/ChatGPT usage windows. Drop the "oauth" default
(missing credential type => no OpenAI usage provider) and thread the turn's
authMode through at the call site. Adds provider-usage.limits.test.ts covering
api-key/no-credential (no fetch), oauth/token (resolves), and non-OpenAI.
2) usageState is documented as best-effort, present only on live dispatcher
delivery. Routed durable and recovered queue replays re-run this hook as a
stateless transform over the original payload (see QueuedDeliveryPayload); a
point-in-time usage snapshot is not stateless and would replay stale after a
restart, so it is intentionally omitted there. Consumers must treat the field
as optional.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Attach a per-turn execution snapshot to the reply_payload_sending hook as
`usageState`, so a plugin (or the future in-core /usage renderer) can render a
per-response usage readout as a pure consumer of the contract — no side calls.
Recorded in agent-runner, consumed in dispatch. Fields: provider, model,
resolvedRef/requested, reasoningEffort, fastMode, fallbackUsed, is_override
(overrideSource), authMode, compactionCount, contextTokenBudget, token usage,
turn cost (USD), duration, owning agentId/sessionId, chatType, the agent
identity (name/emoji), and the active provider's subscription `limits` windows.
reply_payload_sending is the one reply hook universal across every surface
(incl. the Codex app-server, which emits no llm_output/agent_end), so it is the
correct harness-agnostic place for per-turn usage. Limits are resolved by a
core-internal non-blocking SWR helper (src/infra/provider-usage.limits.ts) and
attached to the snapshot — no new plugin-SDK accessor. All fields optional/additive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(qa): run vitest and playwright scenarios from qa suite
* fix(qa): harden scenario suite dispatch
* refactor(qa): share scenario path utilities
* refactor(qa): share test file scenario runner
* refactor(qa): route test file scenarios through suite runtime
* refactor(qa): use explicit suite runtime result kind
* test(qa): write suite evidence artifact
* refactor(qa): clarify suite execution dispatch
* fix(qa): keep test-file scenarios out of flow-only runners
* refactor(qa): export mixed scenario suite runner
* fix(internal-runtime-context): wrap prompt-preface runtime context body in delimiters
When buildRuntimeContextMessageContent constructs the hidden
runtime context prompt block, the body (which may contain
sensitive metadata like relevant-memories, sender info, and
conversation metadata) was not wrapped in the standard
INTERNAL_RUNTIME_CONTEXT_BEGIN/END delimiters. If the model
echoed this context back in its reply, stripInternalRuntimeContext
could only remove the header and notice lines — the sensitive
body leaked through to user-visible surfaces like Feishu
streaming cards.
Wrap the runtime context body in BEGIN/END delimiters so the
existing stripInternalRuntimeContext (which handles delimited
blocks first) can fully remove the entire block.
Closes#92589
* chore: retrigger CI for proof check
* chore: retrigger CI with corrected proof format
* chore: retrigger CI with corrected proof field format
The Windows Hub companion installers are promoted to the main OpenClaw
release via a manual workflow_dispatch, not every release includes them.
The /releases/latest/download/ links resolved to v2026.6.6 which does not
have the OpenClawCompanion assets, causing 404 errors.
Pin the links to v2026.6.5 (the latest release that has the assets) and
add a fallback note directing users to the releases page when a release
is missing the companion installers.
Fixes#92470
* fix(a11y): B-1 — raise muted text contrast to ≥4.8:1 WCAG AA
* fix(ui): C-1 — ChatSidePanel joins glass surface language
* feat(mobile): D-1 — hamburger overlay nav below 900px
- Esc key now closes nav drawer (globalKeydownHandler)
- Nav item tap targets bumped to min-height: 44px + padding: 10px 16px
in the ≤1100px drawer breakpoint (was 40px / 0 12px)
- Hamburger toggle + overlay drawer were already wired in app-render.ts;
this completes close-on-Esc and ensures accessible tap targets
* fix(a11y): B-2 — consistent focus-visible states distinct from hover
* fix(a11y): B-3 — lift all sub-12px text to 12px minimum
* fix(a11y): narrow focus and CSS scope
* fix(a11y): finish focus-visible selectors
Keep valid root and plugin models available when one generated plugin catalog is invalid, while retaining and logging the catalog error.
Fixes#92553.
Co-authored-by: tangtaizong666 <tangtaizhong792@gmail.com>
Persist successful same-channel Slack and CLI assistant replies exactly once in the owning transcript. Preserve delivery-hook output, routed/runtime ownership, custom stores, and authoritative reset/session rotation bindings.
Fixes#92489
Co-authored-by: Andy Ye <35905412+TurboTheTurtle@users.noreply.github.com>
Resolve bundled Fireworks manifest models through core's static catalog so Kimi K2.6 keeps its 262,144-token context limit and nested model compatibility metadata.
Keep the existing dynamic fallback for uncataloged Fireworks IDs and align bundled Kimi reasoning metadata with existing runtime behavior.
Verified with focused tests, extension/core type checks, lint/format, full build, fresh autoreview, required CI, and a live Fireworks Kimi K2.6 embedded run using a real key.
Co-authored-by: Evgeni Obuchowski <evgeni@obukhovski.com>
Preserve the first native Kimi tool-call ID while rewriting repeated replay occurrences to deterministic OpenAI-style IDs and keeping paired tool results aligned. Moonshot responses-family behavior and providers that do not opt in remain unchanged.
Closes#51593
Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Non-control-UI-visible runs previously dropped assistant commentary on the
floor for session message subscribers. Mirror those events to exact session
subscribers, gated strictly on phase === "commentary" so untagged text or
delta frames and final-answer streaming never dual-lane into channel
surfaces. Dialects that emit commentary as untagged deltas should tag the
phase at provider normalization instead.
Co-authored-by: Forge <forge@psiclawops.dev>
Co-authored-by: Chisel <chisel@psiclawops.dev>
Moonshot/Kimi requires reasoning_content on all assistant tool-call messages
when thinking is enabled. After LCM compaction, cross-model fallback, or
session repair, the replayed history may be missing this field, causing a
400 error from the Moonshot API.
Backfill an empty string to satisfy the API schema contract without
fabricating semantic reasoning content. Follows the same provider-owned
backfill pattern already used by Kimi Coding (extensions/kimi-coding/stream.ts)
and DeepSeek V4 (provider-stream-shared.ts).
Fixes#71491
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(moonshot): add Kimi K2.7 Code support
* test(moonshot): surface K2.7 live provider errors
* ci(live): accept Kimi key for Moonshot sweeps
* test(moonshot): verify K2.7 across API regions