* perf(ci): unblock parallel oxlint shards and gate the lint-lane i18n verify
check-lint is the slowest static lane (median 252s): the control-UI
i18n verify runs 86s unconditionally and the three oxlint shards run
serially (89s) because the parallel gate demands 48GB of memory — a
local-Mac threshold that Blacksmith's 32GB 16 vCPU class can never
meet. CI now parallelizes shards at 8+ cores / 24GB+, and the lint case
reuses the existing run_control_ui_i18n manifest gate so diffs that
cannot affect generated catalogs skip the verify while oxlint always
runs.
* chore(ci): print oxlint shard concurrency decision
The lint-lane validation run still executed shards serially on a 16
vCPU runner; one log line exposes the resolved concurrency and the
host resources the decision saw.
The gateway already broadcasts stream:"plan" agent events with typed
{step, status} snapshots; all three clients dropped them. Control UI now
renders an expandable checklist bar above the composer (run-scoped,
cleared on run lifecycle); the shared iOS/macOS chat UI mounts a
Liquid Glass pill (iOS 26 glassEffect, material fallback) between the
message list and composer; Android adds an expandable pill above the
composer bridged through ChatController/NodeRuntime/MainViewModel.
All parsers accept typed and legacy string steps, enforce the
single-in_progress invariant, and scope snapshots and clears to the
owning run. Locale artifacts, Android resources, and the iOS string
catalog carry generated translations for the pill strings.
Shards that pin a short no-output timeout (agents-core stripes) die fast
on the known warm-cache stall, but a cold Vitest module cache makes the
same imports legitimately silent for minutes - the in-job retry then
re-dies at the short limit without running a single test (observed 4x
on agents-core-runner-cli-2 today). Raise the retry attempt's window to
the 300s watchdog floor while keeping the fast first kill.
check-prompt-snapshots costs 200-310s per PR regenerating real prompt
stacks, but snapshots only change when the generator's import graph or
its fixtures do. The manifest now computes that reach (same pattern as
QA smoke gating: always-run surface regex incl. the codex extension
whose test API loads via a dynamic module id, plus import-graph walk
from the snapshot helper; deletions and null diffs fail safe to
running). Unaffected PR diffs skip the lane with an explicit log line;
pushes and dispatches always run.
The agents-core graph can stall emitting nothing after warming Vitest's
module cache; a cli-runner stripe sat silent for the full 300s watchdog
window before its retry passed in seconds (run 29499091157, +5min on a
green PR). Give the family the same 60s no-output timeout the gateway
startup-health group already uses for this stall class.
Fleet-contended runners inflate measured group runtimes ~20-25% over
the serial-run hints (bins hit 300-358s on a loaded 12:1xZ run against
a 260s packed estimate). A 220s cap keeps the slowest bin near the
5-minute PR wall-clock budget under load at the cost of two extra
jobs (24 -> 26).
* fix: clarify SQLite version error message to prevent user confusion
The error message "3.44.6+" was misinterpreted by users as meaning "3.44.6 and above",
when it actually means "3.44.6+ for the 3.44.x series only". This commit clarifies the
error message to explicitly state that only specific patched versions (3.44.6+, 3.50.7+,
and 3.51.3+) are safe, and that SQLite 3.46.1 is not among them.
Changes:
- Update error message in src/infra/node-sqlite.ts to clarify version requirements
- Update test expectations in src/infra/node-sqlite.test.ts to match new error format
- Fix unnecessary template literal expressions flagged by oxlint
The code logic remains unchanged - SQLite 3.46.1 is correctly rejected as unsafe.
* fix: clarify SQLite version error message to prevent user confusion
The previous error message stated '3.44.6+' which users misinterpreted as
'3.44.6 and above', leading to confusion when versions like 3.46.1 were rejected.
The new message explicitly states '3.44.6+ in the 3.44.x series' and
'3.50.7+ in the 3.50.x series' to make it clear that only specific
minor version series received the WAL-reset bug fix.
This matches the SQLite team's actual fix announcement which only
backported the fix to 3.44.x and 3.50.x series, plus 3.51.3+.
* fix(sqlite): align unsafe version diagnostics
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(ci): skip global declaration emission in the CI artifacts build
Declaration emission is ~95% of the tsdown wall clock (measured 132.5s
with dts vs 6.0s without on the main graph; 224.7s in CI). PR CI's dist
consumers only execute runtime JS, so the ciArtifacts profile now skips
global dts and the plugin-sdk export gate validates self-built scoped
declarations (write-plugin-sdk-entry-dts non-canonical mode, 45s local).
The step cache is dropped in self-build mode because its outputs depend
on the SDK source graph the canonical cache inputs do not cover.
Release and docker packaging builds keep canonical declarations.
Local end-to-end: build:ci-artifacts 92s with all gates green.
* fix(ci): pin gateway suites to the proven 2-worker Vitest budget
gateway-core and gateway-methods flaked at 6 workers (hook timeouts,
mock-state res.setHeader TypeError, waitForAssertion misses) on the
first run after the worker-budget raise while passing on two prior
validation runs — the same intermittent contention class media-ui
showed. Pin both like the other timing-sensitive groups.
Validation run 29487321249 measured stripe 2 at 179s (hint said 110)
while stripes 1/3 run light; the index striping concentrates import cost
unevenly. Weight the hints accordingly so no compact bin overpacks past
the 260s target.
* refactor(channels): generalize channel retry runner and retry-after parsing; drop dead configured-state probes
* fix(channels): align retry and metadata contracts
* fix(gateway): allow clearing agent model overrides
* fix(protocol): preserve Swift agent model compatibility
* fix(protocol): preserve null when Swift decodes agent updates
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(line): run post-ack webhook processing on its own admitted work root
LINE acks the webhook and dispatches event processing fire-and-forget on
the same async chain. The HTTP request admission that chain inherited is
released as soon as the route handler returns, and a released admission
refuses subordinate queue work - so every LINE inbound agent turn fails
with "GatewayDrainingError: Gateway is draining; new tasks are not
accepted" even though the gateway is healthy. DMs, group mentions, and
postbacks are all affected; the user-visible symptom is the bot replying
"Sorry, I encountered an error processing your message." to everything.
Add runDetachedWebhookWork to the plugin-sdk webhook-request-guards
surface (a thin wrapper over the gateway independent-root continuation,
the same shape core uses in gateway/server/hooks.ts) and route all three
LINE ack-first dispatch sites through it: the gateway monitor handler
(the live path), and the createLineNodeWebhookHandler / Express
middleware handlers (public webhook building blocks an embedder can
register under the gateway). #65375 unified these three into one ack-first
pattern; keeping the detach consistent avoids re-introducing the same
latent defect in the two that are not on the live gateway path today.
The continuation is reserved synchronously while the request is still
admitted, so the detached processing stays accepted and a real restart
drain can wait for it instead of stranding it mid-turn.
Tests pin every layer: the guards suite proves detached post-ack work is
admitted after the request admission is released (and that the inherited
chain without the helper is refused); the monitor lifecycle suite and the
webhook-node suite assert each dispatch site goes through the detached
root. Red/green verified: reverting any dispatch fails its test.
* fix(plugin-sdk): account for runDetachedWebhookWork in public surface budget
* fix(channels): track detached webhook processing
Co-authored-by: 許元豪 <146086744+edenfunf@users.noreply.github.com>
* docs: refresh generated docs map
* chore(plugin-sdk): refresh API baseline
* fix(webhooks): preserve post-ack ordering
* test(plugin-sdk): satisfy detached work lint
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(ci): scale Vitest workers with runner class and share serial module cache
Compact node-test jobs pinned OPENCLAW_VITEST_MAX_WORKERS=2 regardless of
runner size, so import-bound suites (30-45s of module-graph import per
file) crawled: runner-cli spent 284s on 5s of tests, commands-1 296s on
10s. With serial plans the budget now scales with the class (6 on 8vcpu,
3 on 4vcpu); timing-sensitive groups (tooling, tui-pty, infra-process)
stay pinned to 2 via plan-level env. Serial bins share one Vitest
fs-module-cache path so later plans reuse the first plan's transforms.
The import-bound commands stripes and security suite move to the 8vcpu
class, and packing hints refresh from serial-run measurements (run
29481835688).
* perf(ci): stripe cli-runner suite and pin media-ui worker budget
The agents-core config runs files serially (fileParallelism false guards
shared module state), so raising the worker budget cannot help its
import-heavy cli-runner suite (~35s module import per file, 213s serial).
Stripe it three ways so bins parallelize the imports instead. media-ui
hosts browser locator tests that timed out at 6 workers on the first
validation run; pin it to the proven 2-worker budget. commands-1's hint
drops to its measured 6-worker runtime.