* feat(channels): add channel-owned setup contracts
* test(channels): align legacy setup fixtures
* chore(channels): regenerate config and SDK baselines after rebase
* fix(update): run fresh doctor after current-process core changes
* fix(channels): align add pre-scan with execution precedence
* style(cli): format channels-cli test additions
* fix(channels): restore option-before-positional channel resolution via metadata arity scan
* fix(channels): keep help flags out of metadata arity escalation
* test(update): mock fresh post-update doctor in current-process suites
* style: format review fixes and correct entrypoint mock type
* fix(channels): register only modern contract options for dual-publishing plugins
* test(update): align downgrade suites with fresh-doctor child invocation
* docs(channels): record empty-contract and input-forwarding invariants
* fix(line): keep the shipped --token switch as a channel access token alias
* fix(signal): stop treating exact cross-family loopback endpoints as bind-aligned
* chore(config): regenerate docs config baselines after second rebase
* style: format rebased channels add tests
* fix(channels): enforce field-key and flag-name agreement in setup contracts
* fix(signal): detect container endpoints for bare --http-url setup
* fix(signal): ignore unconfigured accounts in transport collision checks
* fix(channels): validate negated setup flags in contract and normalizer
* fix(signal): preserve existing transport kind when setup detection is unreachable
* style(signal): use direct boolean check in collision guard
* style(signal): type test config literals
* docs(update): record two-read design of fresh-doctor validation gate
* fix(channels): satisfy post-rebase architecture gates
* docs: refresh channel setup map
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(google-meet): reject malformed audio base64
* refactor(meeting-bot): validate node audio centrally
Make the shared meeting-bot owner reject malformed push and pull audio for Google Meet, Teams, and Zoom without plugin-specific callbacks or permissive fallbacks.
Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(agents): give bash stdout and stderr independent decode lanes
stdout and stderr are independent pipes, but the local bash execution path
fed both into one onData callback sharing a single TextDecoder and one
streaming ANSI/OSC sanitizer. A multibyte UTF-8 character split across a
stdout read boundary was corrupted when stderr wrote between its bytes, and
an unterminated OSC on stdout swallowed subsequent stderr output. This
contradicts the documented invariant in shell-utils.ts ('Keep one ANSI
parser per process stream so control sequences can span callbacks').
Tag onData with an optional stream identifier and give each lane its own
TextDecoder and text-transform state; finish() flushes every lane. Untagged
callers keep the single shared lane for backward compatibility.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(agents): harden bash stream isolation
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(cron): system-owned heartbeat monitor jobs replace the interval scheduler
- new internal cron payload kind {kind:"heartbeat"}: execution pokes
requestHeartbeat({source:"interval"}); reported in the protocol job
schema, not accepted from client create/patch
- gateway converges one declaration-keyed monitor job per heartbeat-enabled
agent (schedule every+deterministic phase anchor) at startup and on
config reload; removes monitors for unconfigured agents
- heartbeat runner loses its interval setTimeout machinery; nextDueMs
stays as the cooldown gate, event wakes unchanged
* test(cron): heartbeat monitor regressions; docs for cron-owned cadence
- converge/prune/failure-containment tests for heartbeat monitor jobs
- heartbeat payload run fires an interval wake, no system event
- scheduler tests converted from timer self-fire to wake-queue pokes;
timer-mechanics-only tests deleted with the timer
- persisted-shape accepts the heartbeat payload kind
- docs: heartbeat cadence ownership + system payload kind
* fix(cron): heartbeat monitor review round 1
- targeted cron-monitor interval ticks use the full per-agent path so
due-commitment sessions still deliver
- cron-disabled gateways keep a local fallback interval timer (shipped
cron.enabled=false contract; removed when heartbeat config folds into
cron in #110950)
- heartbeat job reconciliations serialize with latest-wins epochs and a
bounded 30s retry after a failed convergence pass
* fix(cron): chain clamped fallback heartbeat timers past the setTimeout cap
* fix(cron): heartbeat monitor review round 3
- targeted monitor redirect skips wakes carrying heartbeat overrides and
surfaces the per-agent terminal skip reason instead of not-due
- cron-disabled fallback timer re-arms with a 1s floor after each firing
so a dropped wake cannot end the chain
- heartbeat payloads are system-owned at the service boundary: add requires
the gateway opt-in, patches to the kind are rejected
* fix(cron): heartbeat monitor review round 4 — full ownership enforcement
- prune only jobs proven to be monitors (prefix AND heartbeat payload)
- existing monitors reject every update patch; declarative upserts on the
monitor key require the gateway opt-in even with a different payload
* fix(cron): complete heartbeat monitor ownership boundary
- converge scopes declarative matching to real monitors so a colliding
user job with the same key is never adopted or overwritten
- monitor removal requires the gateway systemOwned opt-in; ad-hoc
API/CLI deletion is rejected, reconciliation cleanup still prunes
* docs(cron): record intentional enrollment-snapshot semantics for monitor ticks
* fix(cron): repair heartbeat monitor CI gates
* fix(pid): treat EPERM as alive in isPidAlive
Match isPidDefinitelyDead: process.kill(pid, 0) throwing EPERM means the
PID exists but cannot be signaled, so it should not look dead.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(pid): check Linux zombies after EPERM existence probe
EPERM means the PID exists but cannot be signaled; still run the zombie check so Linux zombies are not reported as alive.
Co-authored-by: Cursor <cursoragent@cursor.com>
* test(pid): isolate EPERM liveness probe
Co-authored-by: stantheman0128 <stanshih888@gmail.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
maybeRepairLegacyFlatAuthProfileStores rewrote the per-agent SQLite auth
profile store with a store built solely from the legacy flat
auth-profiles.json, and backed up only that flat JSON. Any credential
present in SQLite but absent from the flat file (for example an OAuth
refresh token from a login after the SQLite migration) was destroyed and
was not in the backup, so a routine openclaw doctor caused unrecoverable
credential loss.
Load the existing SQLite store and merge the legacy flat profiles into
it, preserving credentials already present, then verify the imported
profiles persisted before removing the flat file, mirroring the SQLite
migration path.
* docs(plugins): link setup-input deprecations to the migration pattern and document reader sweeps
* ci: retrigger queued run
* docs: regenerate docs map
1. Centralize conflict projection, supersession, cleanup, and settlement across live turns, recovery, and reclaim while keeping resume and reclaim terminal states explicit.
2. Reuse an entries-only changed-path helper for file and symlink staging.
3. Reuse the unchanged-branch reconcile preflight when no filesystem mutation occurred.
4. Move all quiescence scripts into one owner and share their process and lease engines. Deliberate micro-change: give quiesce processes() the same 2000 ms ps timeout added to renew by #112186.
5. Replace active-turn-claim message matching with ActiveTurnClaimError while preserving the message.
6. Document each staged-finalize stability and active-lease fence by its protected race.
7. Remove the unshipped locale-collated accepted-ref fallback and locale argv plumbing. Writer 8631832048 was replaced by canonical collation in 792f5b7b74e433f9471eed5abcb0fc334d28bdeb; git tag --contains returned no release tags for either commit.