* fix(process): report actual UTF-8 byte count in stdin write message
Replace string.length (UTF-16 code units) with Buffer.byteLength(content, 'utf8')
so the reported byte count matches the actual bytes written to process stdin.
For ASCII content the values are identical. For non-ASCII content (CJK, emoji),
string.length underreports the actual UTF-8 byte count.
* test(process): cover UTF-8 stdin write byte count
Co-authored-by: 陈志强0668000989 <chen.zhiqiang1@xydigit.com>
* style(test): keep UTF-8 regression compact
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(process): report actual UTF-8 byte count in send-keys message
Replace string.length (UTF-16 code units) with Buffer.byteLength(data, 'utf8')
so the reported byte count matches the actual bytes sent to process stdin.
For ASCII key sequences the values are identical. For non-ASCII text
sent via literal key data, string.length underreports the actual UTF-8
byte count.
* test(process): cover UTF-8 send-keys byte count
Co-authored-by: 陈志强0668000989 <chen.zhiqiang1@xydigit.com>
* ci: prune stale max-lines baseline
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* 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
* fix(plugins): keep official plugin config across bundled-to-external moves, harden npm metadata parsing
- doctor --fix and plugin auto-enable treat official-catalog plugin ids as
known configuration even when the external package is not installed yet, so
a bundled-to-external transition (codex) no longer silently drops the
plugins.allow entry and unregisters the agent harness; garbage ids are still
pruned, and the codex-specific diagnostic suppression in the doctor scan is
replaced by the generic official-catalog rule (#107226)
- npm view metadata normalization handles object and multi-version array
output, flat dist.* keys and nested dist objects, absent openclaw blocks,
selects the maximum satisfying semver (publication order is not semver
order), fails closed when a recognized range has no satisfying entry, and
names the missing fields in the error (#107842)
The #107219 register-crash fix (lazy binding-store opening so plugin
registration never touches runtime.state under the base runtime) landed with
#108311 alongside its regression test.
* style: format install-source-utils imports
* fix(plugins): align hook and tool registrations
Reuse gateway-owned plugin registrations for matching hooks and tools while loading only missing tool owners from narrower runtime scopes.\n\nCo-authored-by: w33d <w33d@steadholme.local>
* test(plugins): cover mixed registry tool owners
Verify gateway-pinned and compatible active registrations compose without another plugin load.
Co-authored-by: w33d <w33d@steadholme.local>
* refactor(plugins): keep pinned registry lookup internal
Reuse the existing runtime-state contract so hook and tool ownership does not expand the Plugin SDK surface or treat unpinned active registries as Gateway owners.
* test(plugins): complete hook context fixture
Supply the required tool name in the stateful hook ownership regression context.
* chore: leave contributor release note in PR
Normal contributor PRs do not modify the release-owned changelog; the PR body retains the release note and attribution.
* fix(plugins): align partial registry owners
* fix(plugins): preserve scoped tool diagnostics
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: w33d <w33d@steadholme.local>
* fix: treat loopback bind as local-only in doctor security
* test: type resolveGatewayBindHost mock for check-test-types
* fix(gateway): keep loopback bind canonical
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
* fix(web-fetch): recognize mixed-case media types
* fix(web-fetch): match normalized media types
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
Move the Codex-specific PreToolUse loop relay switch into the Codex
plugin, preserve policy relays, and cover both normal and side turns.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: marchpure <marchpure@users.noreply.github.com>
* chore(plugins): gate deprecation hygiene in CI and purge internal deprecated usage
* fix(agents): migrate auth-profiles oauth import to OAuthProviderId
* fix(whatsapp): use durability param in delivery-recovery test
* chore(scripts): declare shouldRunDeprecationHygieneChecks in check-changed types
* test(plugins): update runtime-api guard for ssrf policy rename
* test(codex): use allowlisted placeholder for auth-bridge api-key fixture
* fix(codex): model-scoped usage-limit blocks, structural 429 classification, no silent API-key billing
- usage-limit blocks written from Codex rate-limit resets are model-scoped via
a persisted blockedScope marker: healthy sibling models on the same auth
profile stay usable; a different/unknown model failing widens the block
profile-wide and never narrows back; legacy rows without the marker stay
profile-wide until they expire (#100556)
- Codex usage-limit failures now surface as status-429 Error objects at both
ingress paths (turn-start and streamed turn failure, wrapped at the event
projector), so core failover classification is structural instead of
matching message wording; profile blocking uses only rate-limit data whose
revision advanced during the turn
- usage-limit detection requires the structured codexErrorInfo signal; the
over-broad "usage limit" substring match that misclassified unrelated
errors as subscription limits is gone (#96815)
- subscription/OAuth routes can no longer silently fall back to env/auth.json
API keys: ambient key fallback is restricted to explicit api-key routes,
native-auth subscription routes verify the account is chatgpt-backed via
account/read, and shared app-server clients are partitioned by auth
requirement so pooled clients cannot cross billing modes (#106375)
- integrated e2e regression: usage-limit promptError -> same-model sibling
profile rotation -> model fallback with reason rate_limit
* chore(codex): keep CodexUsageLimitErrorResult type local
Adds a Discord Activities integration so an agent can show a self-contained
HTML widget to Discord users, opened as a sandboxed Activity inside the client.
Off by default: routes, the discord_widget tool, and the launch handler register
only when channels.discord.activities is configured. OAuth identifies the user
and gates on the account allowlist; widget lookup is capability- or
instance-validated; token exchange is rate-limited; widget HTML runs in a
no-network sandboxed iframe.