* fix(acpx): stop/start races cannot resurrect a stopped runtime backend
The deferred acpx runtime service published its lazily started backend without
lifecycle ownership checks: a stop during in-flight activation let the stale
service become the active runtime, and stop cleared shared state while startup
still used it. Start/stop now use a lifecycle revision; only the current
revision may publish a runtime, stop invalidates every deferred proxy before
awaiting in-flight startup, and restart waits for the previous stop to settle.
* chore: re-fire CI
* fix(acpx): fence deferred runtime ownership
* refactor(acpx): centralize backend lifecycle ownership
* fix(acpx): annotate deferred runtime cycle
* fix(plugins): import adopted catalog history into the OpenClaw transcript
Import native Pi and OpenCode history when a catalog session is adopted so the OpenClaw transcript reflects the full conversation immediately.
Mark imported user rows with mirrorOrigin because they are transcript mirrors, not new external turns. This preserves watcher deduplication and prevents adopted history from being reported as fresh upstream input.
* refactor(plugins): split catalog history importer
Keep the session catalog contract module leaf-like by moving transcript mutation into a dedicated runtime module. This breaks the registry-to-transcript import cycle while preserving the Plugin SDK entrypoint and importer behavior.
* feat(plugins): support additional upstream session kinds
* feat(acpx): monitor upstream Pi sessions
* feat(opencode): monitor upstream OpenCode sessions
* fix(opencode): ignore hidden user text activity
* refactor(plugins): share upstream echo filtering
* fix(opencode): preserve marker tuple narrowing
* refactor(opencode): detect upstream turns via event_sequence cursor
Replace timestamp/grace-window completeness heuristics with OpenCode's own
per-session event_sequence.seq cursor, which advances inside the same
transaction as the message/part projections. Detects change instead of
inferring completion, so staged part writes can no longer drop a turn.
Handles cursor regression from OpenCode migrations that clear event_sequence,
suppresses compaction replay and summary re-publish, and restricts human-turn
classification to visible non-synthetic text.
* docs(plugins): record why the adoption coordinator requires complete
* fix(opencode): align cursor proof with strict types
* fix(acpx): keep Pi JSON parser private
* fix(acpx): keep leaked non-openai model out of the Codex ACP thinking slot
Codex ACP spawn mis-routed an inherited non-OpenAI fleet default into the
reasoning-effort slot and aborted (#95780). Replace the splitter with a closed
classifier and make the spawn path provenance-aware: drop an inherited leaked
default so Codex starts on its own default, but fail closed with
ACP_INVALID_RUNTIME_OPTION when a caller explicitly selects an unsupported or
malformed model. Thread a modelExplicit flag from resolveAcpSpawnRuntimeOptions
through the ACP runtime ensure contract; strip it before the acpx delegate.
Dropping the inherited default only at ensureSession was not enough: the manager
still persisted the leaked model in runtimeOptions, and the first turn replayed
it through applyRuntimeControls -> setConfigOption(model), which the new
fail-closed Codex control path then rejected. ensureSession now reports the
effective model it applied on the returned handle (applied | dropped), and the
manager persists that effective model, so a dropped inherited default is never
saved or replayed as a model control before the first turn. Explicit unsupported
selections still fail closed at spawn and never persist.
* test(acp): split runtime config validation tests
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(acpx): migrate codex adapter to maintained @agentclientprotocol/codex-acp
The bundled codex ACP adapter pinned the npm-deprecated
@zed-industries/codex-acp@0.16.0, which embeds codex crates from the
rust-v0.137.0 era: GPT-5.6-era models are unknown to it and sessions fail
with 'requires a newer version of Codex'. Migrate to the maintained
@agentclientprotocol/codex-acp@1.1.4 (Node CLI, depends on @openai/codex
^0.144.4 matching the codex plugin pin):
- Wrapper generation resolves the adapter bin from its package manifest and
delivers startup config via CODEX_CONFIG; OpenClaw-owned wrapper args are
stripped before launch. Explicit new-adapter commands keep their own
-c/--config args untouched; only retired Zed commands migrate, with their
-c TOML overrides (dotted/quoted keys, attached forms, MCP config)
faithfully converted.
- Model overrides emit the bare model id plus a separate
model_reasoning_effort; composite model/effort ids are accepted as parse
input but never emitted.
- Process reaper recognizes the maintained adapter tree and packaged codex
app-server children without claiming native codex plugin processes.
Live proof: direct adapter smoke with local subscription auth initialized
1.1.4, advertised model gpt-5.6-sol with separate medium effort, and
completed a turn. Fixes#108664
* fix(acpx): pin codex-acp 1.1.2 to satisfy the lockfile release-age gate
1.1.4 is under the 48-hour minimumReleaseAge and fails lockfile
verification repo-wide. 1.1.2 (2026-07-09) passes the gate, carries the
same CODEX_CONFIG startup contract, and its ^0.144.0 codex dependency
resolves to the same 0.144.4 the codex plugin pins. Lockfile re-resolved
from main without any release-age bypass; shrinkwrap regenerated.
* chore(acpx): split oversized auth-bridge test file, register adapter as CLI-resolved dep
codex-auth-bridge.test.ts crossed the max-lines gate after the migration
regressions; the command-migration cluster moves to
codex-auth-bridge.migration.test.ts. knip's CLI-resolved dependency list
swaps the retired zed adapter for @agentclientprotocol/codex-acp (invoked
via generated wrapper/npx, never imported).
* fix(acp): skip unsupported automatic thinking config in manager gate
When an ACP backend does not advertise thinking-related config options
(e.g. OpenCode advertising only mode/model), the manager's automatic
startup-control path would reject the session with
ACP_BACKEND_UNSUPPORTED_CONTROL.
Fix:
- Add THINKING_CONFIG_KEYS set and isThinkingConfigKey helper to
manager.runtime-controls.ts, following the existing OPTIONAL_TIMEOUT_CONFIG_KEYS pattern
- Skip thinking keys in the advertised-keys validation gate (auto-derived
options are best-effort)
- Skip unsupported-control rejection for thinking keys at the
setConfigOption catch level
- Extract isUnsupportedControlRejection helper to avoid duplicating the
error code check
- Forward getCapabilities input handle in ACPX runtime so session-scoped
capability queries work
- Inject thinking/thought_level aliases for Codex ACP sessions when
reasoning_effort is advertised
Closes#103802
* fix(acp): honor thinking control capabilities
---------
Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(memory-lancedb): gate auto-recall and auto-capture on per-agent memorySearch.enabled (#103590)
The before_prompt_build auto-recall hook only checked the plugin-level
autoRecall flag, so agents configured with memorySearch.enabled: false
still received <relevant-memories> injected from the shared LanceDB store
- leaking one agent's private memories into another agent's prompts. Gate
both recall injection and agent_end auto-capture on the current agent's
memorySearch.enabled (per-agent entry wins over agents.defaults; unset
means enabled), mirroring core resolveMemorySearchConfig semantics.
* fix(memory-lancedb): normalize agent ids before the memorySearch gate
Review follow-up on #103799: a configured id like 'XiaoHuo' or one with
surrounding whitespace missed the exact-match per-agent override and
inherited the enabled default, leaving the disclosure path active.
Normalize both the hook agent id and configured entry ids with the SDK
normalizeAgentId before comparing.
* fix(memory-lancedb): resolve the per-agent memorySearch gate via resolveAgentConfig
* fix(memory): isolate LanceDB rows by agent
Co-authored-by: Shubhankar Tripathy <reach2shubhankar@gmail.com>
* fix(memory): isolate LanceDB rows by agent
Co-authored-by: Shubhankar Tripathy <reach2shubhankar@gmail.com>
* refactor(memory): keep LanceDB store types private
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* refactor(extensions): remove dead QA and utility exports
* refactor(extensions): trim Matrix QA internal exports
* chore(deadcode): refresh extension export baseline
* chore(ci): reconcile deadcode and LOC baselines
* chore(deadcode): refresh baseline after main advance
* chore(deadcode): refresh baseline after main advance
* refactor(plugins): keep channel snapshot type private
* fix(ci): align Linux deadcode baseline
* fix(extensions): make indexed access explicit across channel plugins
Transport-payload-safe burn-down: malformed Telegram/Discord/QQ/LINE
and sibling channel input keeps existing skip paths; no synthesized
fields, no new throws in delivery loops. Zalo escape sentinels preserve
literal matches instead of undefined replacements.
* fix(extensions): make indexed access explicit across provider and memory plugins
Stream and model iteration, tool-block guards, capture guards, and
sparse accumulators; singleton model reads carry named invariants.
* fix(extensions): make indexed access explicit across tooling plugins, flip the extensions lane
Remaining plugins (oc-path, qa-lab, browser, logbook, and siblings) plus
the tsconfig.extensions.json flag flip. Cleanup: logbook sampleFrames
NaN index at max=1, QA retry clamp at non-positive attempts, dead Canvas
probe and OpenShell no-op slice removed, twitch test setup leak excluded
from the prod lane.
* refactor(plugin-sdk): expose expectDefined via a focused SDK subpath
Extensions imported @openclaw/normalization-core directly, crossing the
external-plugin packaging boundary (it only worked because the runtime
builder bundles undeclared workspace helpers). expect-runtime joins the
canonical entrypoints JSON, generated exports, API baseline, docs, and
subpath contract test; all 78 extension imports now use the SDK seam.
Two scanner-shaped locals renamed for review-bundle hygiene.
* chore(plugin-sdk): raise surface budgets for the expect-runtime subpath
One new entrypoint with one callable export, added intentionally as the
packaging-honest seam for extension invariant helpers.