* 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(qa-channel): add timeout to qa-bus state guarded fetch
* test(qa-channel): add executable negative control for bus-state hang
* test(qa-channel): oxfmt negative-control bus-client test
* fix(qa-channel): sync SDK facade type with getQaBusState options parameter
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* test(qa-channel): add standalone production-path proof for bus-state timeout
Proof exercises the real getQaBusState code path against loopback
TCP peers that accept but never return HTTP headers:
- Negative control: fetchWithSsrFGuard without timeoutMs stays pending
- Positive control: getQaBusState with timeout rejects with TimeoutError
- Valid response: normal server still resolves within the timeout floor
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* test(qa-channel): log node version and head SHA in bus-state timeout proof
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* fix(qa-channel): drop test timeout from public Plugin SDK facade type
Keep the `timeoutMs` option internal to `getQaBusState` for test-only
short floors. The public facade callers do not need it and the SDK
surface should not expose test-only parameters.
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* chore(qa-channel): remove standalone proof script per review
The negative, timeout, and responsive cases are already durably covered in
bus-client.test.ts. The executed live output remains in the PR body as
evidence.
Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>
* chore: trigger CI after rebase onto main
* chore: trigger CI after rebase onto main
* test(qa-channel): keep state timeout internal
---------
Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
* 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(codex): settle plugin activation, align plugin/list with codex 0.144, bound discovery per turn
- plugin/list curated queries omit cwds and marketplaceKinds (0.144 semantics:
explicit kinds disable the automatic global remote catalog; cwds:[] never
suppressed it), and the missing-marketplace check recognizes the current
openai-curated-remote wire name via the shared predicate (#107305)
- curated plugin/list snapshots settle in a process-local metadata cache
(coalesced, invalidated on install/identity change/restart, 1h freshness
window matching the app-inventory cache) so a missing marketplace or plugin
no longer re-runs blocking discovery RPCs on every embedded-Codex turn;
fail-open local-only responses (upstream warns without a load error when the
remote catalog fetch fails) are never cached, and workspace-directory
queries stay live because external activation has no invalidation signal
- the whole plugin-config build shares one bounded startup deadline with
remaining-budget propagation per RPC and a deny-all apps fallback, so a hung
plugin/list cannot consume the turn (#107305)
- guarded thread requests (start/resume/fork under the native-config fence)
must carry a finite timeout or abort signal, closing the unbounded-fence-hold
window for raw callers (#106719 hardening)
* chore(codex): keep plugin metadata types and deadline builder module-local
Deadline behavior tests exercise the production provider composition instead
of a test-only export.
* 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>