* fix(scripts): bound tsgo runs with the managed-command watchdog
run-tsgo bypassed the repo's managed-command seam and called spawnSync
directly, so a wedged tsgo blocked its caller indefinitely: no timeout, no
process-group cleanup, and no SIGKILL escalation.
Observed in the wild as a tsgo holding 2.85 GB for 90+ minutes on 41s of
total CPU with RSS frozen to the byte, ignoring SIGTERM, with its wrapper
reparented to init. Because shouldReclaimLock() treats a live PID as a valid
lock owner, that orphan also held the heavy-check lock until every other
invocation hit the 10-minute lock timeout and threw.
Route the run through runManagedCommand, which already owns process-group
termination and SIGKILL escalation on timeout, and bound it with
OPENCLAW_TSGO_TIMEOUT_MS (default 45m) through the shared readPositiveEnvInt
helper, mirroring OPENCLAW_CLI_STARTUP_BUILD_TIMEOUT_MS in
ensure-cli-startup-build.mts.
* fix(scripts): saturate the tsgo watchdog at Node's timer ceiling
An OPENCLAW_TSGO_TIMEOUT_MS above 2147483647 reached setTimeout unchanged,
where Node collapses it to a 1ms delay, so raising the override killed
healthy typechecks immediately instead of loosening the bound.
* fix(scripts): make the tsgo watchdog opt-in and stop the harness leaking
ClawSweeper review on 6ba02c9d0a raised two findings.
[P1] The 45-minute default applied an unproven deadline to every tsgo
invocation. No supported duration contract covers every host and project, and
CI already bounds its own tsgo jobs at 15-20 minutes, so the default could only
ever fire outside CI where it was least validated. Drop it: an unset
OPENCLAW_TSGO_TIMEOUT_MS keeps the pre-existing unbounded wait, so no existing
run changes behavior, and operators opt in per host. Documented in
docs/help/testing.md beside the sibling Vitest watchdog.
[P2] The regression harness could leak its wedged child. The fake compiler
ignores SIGTERM by design, so a pre-fix or otherwise failing run left the tree
running after spawnSync gave up. Bound the fixture's loop as a backstop.
* fix(scripts): set the tsgo watchdog default from measured lane duration
ClawSweeper on c7a699ee82 reversed its earlier guidance: the opt-in default
adopted last iteration "deliberately preserves the indefinite tsgo hang that
this PR is meant to fix". Its objection was never that a default existed, only
that 45 minutes was unmeasured.
Measured instead of guessed: hosted tsgo lanes (check-test-types, and its core
stripes) complete in 1-2 minutes across recent successful main runs, against CI
job caps of 15-20 minutes. 30 minutes is 15-30x the observed duration, leaves
room for a far slower local host, and still bounds the 90-minute and multi-hour
wedges that motivated this PR. OPENCLAW_TSGO_TIMEOUT_MS remains the documented
override for hosts that need longer.
* test(scripts): reap the wedged fake tsgo tree on the harness outer timeout
ClawSweeper on 0d9f3604e8 flagged that the harness can still leave a detached
pre-fix process tree alive after its outer timeout. The bounded fixture loop
added earlier only capped the leak; it did not terminate the tree.
spawnSync's killSignal reaches the direct child only. runManagedCommand spawns
the compiler detached into its own process group, so the fake tsgo is a
grandchild that never receives that signal. The fixture now records its pid and
the harness reaps that group in a finally, with the bounded loop kept as a
last-resort backstop.
Verified: pid file written with the live pid, and killing that group terminates
the tree; focused suite 16/16 with no surviving fake-tsgo processes.
* fix(scripts): harden the tsgo watchdog after two-phase code review
Review fixes on top of the watchdog change, from one native pass and six cold
passes:
- A rejected OPENCLAW_TSGO_TIMEOUT_MS escaped main() as a raw module rejection.
It now reports one actionable line and exits 1. Strict validation was kept
rather than switching to coercion, so a typo cannot silently fall back to the
30-minute default.
- The rejection message named a numeric range while the parser enforces plain
decimal digits, so 1e5 and 007 were refused by a message saying they
qualified. It now names the real format and states that the watchdog cannot
be disabled.
- The timer ceiling is declared locally rather than imported from packages/.
A static import there resolves before the sparse-checkout guard runs, which
turned a clean sparse skip into ERR_MODULE_NOT_FOUND and flipped
check-changed's typecheck lane from exit 0 to exit 1.
- The wedge test asserted the kill message but not the outcome; it now captures
the wedged pid and asserts the process group is gone.
- Three near-duplicate "not killed" cases are table-driven.
- Doc bullet corrected: values ABOVE the ceiling saturate at it, and the
rejected-value list now includes non-decimal input.
Deferred follow-up, not fixed here: scripts/lib/tsx-cli-shim.mjs shares a
5000ms force-kill delay with managed-child-process, so Ctrl-C can still orphan
a wedged compiler about one run in three. Measured base 4/4 orphaned versus
4/10 here, so this change improves it; the fix is out of diff and shared with
four other wrappers.
* fix(scripts): close tsgo signal cleanup race
* fix(scripts): make tsgo watchdog opt-in
---------
Co-authored-by: ClawSweeper <steipete+clawsweeper@gmail.com>
With agents.defaults.heartbeat.every set to "0m", a background exec completion queued its system event and requested a targeted exec-event wake, but the shared unscheduled-wake policy only admitted immediate wakes, so the follow-up agent turn silently never ran. Admit the exact exec-event/event producer shape (session or agent target required) in one per-source predicate, dispatch wake timers outside the requesting attempt's transcript-writer context, and document that "0m" disables recurring cadence only.
Closes#62505
Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Give maintainers immediate visibility when Mantis is requested. Bare mentions now react, link the active run, and keep one run-owned status comment through proof, short-circuit, or failure.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Add trusted ClawSweeper-label and maintainer-comment dispatch for Mantis Telegram proof. Short-circuit non-visible PRs before desktop setup while preserving exact-head, fork, credential, and comment-ownership boundaries.
* refactor(plugin-sdk): replace API baselines with diffs
* perf(plugin-sdk): bound API diff resources
* fix(plugin-sdk): isolate API diff dependencies
* fix(release): forward Plugin SDK acknowledgement
* fix(release): enforce SDK acknowledgement on publish
* chore: preserve generated-doc ignore policy
* fix(release): freeze SDK API evidence before publish
* fix(ci): satisfy SDK evidence guards
* fix(release): bind complete SDK evidence
* fix(release): authenticate plugin SDK evidence
* fix(plugin-sdk): abort interrupted API diffs
* test(ui): freeze page clock in background-tasks rail e2e
The rail transcript is compared byte-for-byte across the detail-panel
round-trip while it renders live relative ages; on slow CI runners the
second boundary ticks between the two reads (11s -> 12s) and fails the
equality assertion. Fix the page Date with Playwright setFixedTime while
keeping timers running so the tasks.list polling assertions still hold.
Repro: a 1.5s stall between the reads fails pre-fix with the exact CI
diff and passes post-fix.
* fix(scripts): drop unused export on dependency-evidence CLI main
Knip's workflow scan re-roots script references after an actions/checkout
step that sets path:, so the new trusted-tooling checkout in
openclaw-npm-release.yml stops marking this CLI as a workflow entry and
its exported main() surfaces as an unused export in check-dependencies.
Nothing imports main; the module invokes it through its own entry guard,
so the export keyword was dead surface either way.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(xai): add Grok 4.6 catalog and preserve OAuth xhigh
Add first-class Grok 4.6 discovery, pricing, and reasoning metadata while
keeping the existing Grok 4.3 API-key and server-tool defaults unchanged.
Preserve xhigh only for Grok 4.6 so OAuth auto no longer inherits the
Grok 4.5 high downgrade.
Closes nothing; tracks #122734.
* fix(xai): resolve the OAuth auto alias to its canonical model in the thinking policy
The OAuth catalog row keeps id "auto" and records the provider-selected
target in params.canonicalModelId; judging the raw alias collapsed the
default OAuth route to an off-only thinking picker for every model.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* test(xai): refresh catalog-order assertions and restore stream deadlines for Grok 4.6
Exact-order catalog and onboarding arrays now include the new row with its
metadata assertion; payload-capture timeouts return to their original
values, Grok 4.5 off-clamp and Grok 4.3 modern-model coverage are
restored, and the Grok 4.6 xhigh boundary test passes its id explicitly.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(agents): add Grok 4.6 to the curated high-signal live matrix
xai is a curated-only high-signal provider, so the new flagship was
structurally excluded from default live sweeps; add it alongside
Grok 4.5 with its test and docs mirrors, matching the 4.5 precedent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(xai): align Grok frontier metadata
* fix(xai): align Grok 4.6 catalog contract
Remove the unsupported moving alias, centralize OAuth auto target resolution, correct cached pricing, and keep live coverage on the stable xhigh completion path.
Co-authored-by: Kiran Magic <262980978+kiranmagic7@users.noreply.github.com>
---------
Co-authored-by: Kiran Magic <262980978+kiranmagic7@users.noreply.github.com>
Co-authored-by: Kiran Magic <kiran@Alices-Laptop.local>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Kiran <kiranmagic7@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(ai): preserve Responses server compaction state
Persist opaque Responses compaction items as fenced provider replay state so long stateless sessions can resume authoritative compressed history without exposing it in display or diagnostics. Carry state through worker transcripts and prune replay prefixes without splitting tool pairs.
Release note: Preserve long OpenAI Responses sessions across server-side compaction and worker restarts.
Related: #95788
* test(ai): align long-context fixtures with CI contracts
Make tool-result fixtures type-complete, use the canonical model selector helper, remove unused test-helper exports, and route the paid long-context live probe through the dedicated Gateway profile shard.
* test(ai): type mocked Responses terminal events
Give the mock SSE event collection an explicit open event shape so terminal response events coexist with output-item events under the root test typecheck.
* fix(ai): suppress rejected compaction replay
Persist a route-fenced suppression tombstone when encrypted-content recovery rejects a compaction item, so later turns do not retry the same opaque state. Preserve the tombstone through transcript redaction and cover successful fallback followed by the next turn.
* fix(ai): keep compaction suppression transport-private
Keep the suppression contract local to its sole Responses transport owner and make the regression fixture satisfy root type and lint checks without widening the Plugin SDK surface.
* refactor(ai): remove compaction suppression re-export
* fix(ai): scope compaction suppression to replay route
Keep foreign-route rejection tombstones from hiding the newest compatible Responses compaction while preserving same-route suppression.
* fix(ai): harden Responses replay recovery
Stage encrypted replay recovery so compaction is only suppressed after an attributable rejection. Preserve terminal ordering and keep provider replay within worker frame budgets without truncating opaque state.
* refactor(ai): centralize Responses output indexes
Keep normalized output identity tracking in the stream-slot owner, move response failure state to its diagnostic owner, and remove the obsolete replay clone export so exact-head static gates remain shrink-only.
* fix(ai): retain idless terminal tool identity
Use the canonical empty identity only when a provider supplies neither call nor item id, preventing terminal recovery from duplicating a done-only tool call while preserving stronger identities when available.
* fix(sessions): hide provider replay from public events
* fix(ai): stage encrypted replay recovery
* fix(ai): keep replay attempt kind internal
* fix(ai): route Azure through replay recovery
Use the shared encrypted-content retry owner for Azure Responses so compaction suppression and prompt-observer variants stay coherent across transports.
* fix(ai): harden replay persistence boundaries
Fence Azure replay by the resolved request endpoint, drop invalid replay during transcript sanitization, and surface worker-launch replay omissions through the existing redacted diagnostic path.
* fix: gateway service commands refuse a named profile or relocated OPENCLAW_HOME
- Resolve the default install identity against the canonical state directory
for the active OpenClaw home and profile instead of the unprofiled OS
account default.
- `--profile <name>` / `--dev` project `.openclaw-<profile>` state and config
paths, so every named profile was classified as isolated state and refused
`install`, `start`, `stop`, `restart`, `uninstall`, Doctor service repair,
and self-update service handling.
- `OPENCLAW_HOME` relocates all OpenClaw path defaults and is documented for
running as a dedicated service user; a relocated home is now an install
identity. `HOME` alone still is not.
- An `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` pointing outside those
canonical paths is still treated as isolated state.
- Recovery guidance in the refusal message now names the paths that must match.
Verified: focused vitest shards for the changed suites plus the daemon, CLI,
and doctor suites that consume the identity check; tsgo core and core-test
lanes; oxlint; docs format, MDX, link, and map checks.
* fix(gateway): keep relocated homes isolated
* fix(config): validate service profile identity
* fix(daemon): enforce named-profile service ownership
* fix(update): reject drifted service selectors before probes
* test(windows): prove scheduled task lifecycle
* test(windows): harden scheduled task proof cleanup
* test(windows): bind lifecycle proof to checkout
* test(windows): normalize cleanup exit status
* test(windows): verify effective task privilege
* test(windows): protect scheduled task proof roots
* test(windows): prove listener-owned task lifecycle
* test(windows): fix scheduled task proof contracts
* test(windows): remove redundant mock coercions
* test(windows): measure fallback before task probes
* test(windows): prove scheduled task process origin
* fix(gateway): preserve unmanaged restart fallback
* test(gateway): cover denied restart ownership
* test(gateway): keep restart helper types private
* test(gateway): classify lifecycle helpers as test code
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
The gateway.reload documentation advertised four settings the shipped
build rejects. gateway.reload is a strictObject accepting only `mode`,
and `mode` is a union of "off" | "hybrid", so a config written from the
docs fails validation and the gateway fails closed on the unknown keys.
Retired but still documented:
- `debounceMs` and `deferralTimeoutMs` (RETIRED_TUNING_PATHS; stripped by
`openclaw doctor --fix` via stripRetiredTuningKnobs)
- `mode: "restart"` and `mode: "hot"` (mapped to "hybrid" by
`openclaw doctor --fix`)
Correct all four docs pages that still describe them, and say what
replaced them: debounce and active-work deferral now run behind built-in
defaults rather than config keys.
Refs #116973
* docs(automation): rename scheduled-tasks feature wording to Automations
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WhJ8EiMXue6ADLmHfb7FL6
* docs: regenerate docs map and add Automations glossary entries
* docs(templates): follow renamed automations-vs-heartbeat anchor
* docs(automation): fix markdown formatting drift
* docs(automation): teach the canonical automations tool and sync the copied heartbeat default
Review follow-ups: normal instructions use the automations tool with cron as
an explicit compatibility alias; every verbatim copy of the default heartbeat
prompt matches the new shipped text from the strings PR.
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>