* 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(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.
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.
* fix(synology-chat): preserve UTF-8 in user list responses
* refactor(synology-chat): use native response decoding
Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(workspaces): reject non-decimal --grid segments via strict parser
Swap bare Number() in parseWorkspaceGrid for parseStrictNonNegativeInteger
from openclaw/plugin-sdk/number-runtime, so JS-coercion forms (hex,
exponent, octal, binary, negative, empty segment) are rejected at the
CLI --grid parser boundary instead of being silently persisted into
Widget.grid. Same owner-boundary shape as #106384 (google-meet --since)
and #106926 (onepassword --limit).
Co-Authored-By: Claude <noreply@anthropic.com>
* test(workspaces): prove persisted grid coercions
Co-authored-by: wangmiao0668000666 <wangmiao0668000666@users.noreply.github.com>
---------
Co-authored-by: wangmiao0668000666 <wangmiao0668000666@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(codex): use structured turn status for aborts, repair tool schemas, relay native results to middleware
- turn aborts are detected from the correlated turn/completed status instead
of byte-matching private codex-rs prose; the <turn_aborted> marker remains a
non-terminal user-interrupt hint (tags only, wording-independent) because the
app-server collapses all abort reasons into "interrupted" (#99268)
- dynamic-tool schemas are repaired before projection via the shared OpenAI
normalizer: annotation nulls stripped, type:null inferred from object/array
shape hints; unrepairable null constraints stay quarantined instead of
widening the schema (#106277, #97913)
- Codex-native tool results now flow through agentToolResultMiddleware at the
post_tool_use relay boundary (observe-only: the Codex PostToolUse contract
cannot replace a native tool response) (#95597)
* test(codex): regenerate prompt snapshots for normalized dynamic-tool schemas
The shared OpenAI strict-compat normalization now runs on Codex dynamic tools,
so empty object schemas gain additionalProperties:false and required:[] in the
snapshot payloads, matching what the OpenAI provider family sends.
* fix(line): drop blank entries from trailing commas in agenda/device directives
* fix(line): drop blank device action labels
Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>