* fix: surface Codex input prompts across runtimes
Codex structured input now reaches bounded Gateway questions in native and ACP runs, with exact turn ownership, explicit unsupported outcomes, and cancellation fencing. Consume the published ACPX elicitation support.
* chore: align elicitation helpers with current guards
Use protocol-specific helper names required by current main and update the reservation regression to the generalized input owner.
* fix: formalize structured input SDK surface
Expose one documented, frozen agent-harness structured-input contract with runtime and subpath coverage, replacing the accidental function-property API.
* fix: satisfy elicitation architecture gates
Register the real-process ACPX fixture as an executable test root and move shared structured-input types into the boundary leaf to keep Knip and Madge clean.
* fix: remove structured input lint suppression
Preserve the rejected control and invisible-character ranges with an explicit code-point check so the production suppression inventory stays closed.
The ACPX lazy runtime proxy optional-chained every forwarded hook and
fabricated success when the resolved runtime lacked one: doctor() returned
{ ok: true }, getStatus() returned {}, and setMode/setConfigOption/
prepareFreshSession silently no-opped. Combined with the session-reset
service calling prepareFreshSession through two raw optional chains, an
unregistered or incomplete backend made reset preparation a silent no-op:
the reset appeared to succeed while the backend kept resuming the old
conversation.
Contract decision: the SDK-level AcpRuntime type keeps optional hooks
(third-party backends legitimately omit capabilities, and core consumers
gate on presence), but every runtime the ACPX extension resolves
implements the full surface. A new CompleteAcpRuntime type in the
extension makes those hooks required for proxy-resolved runtimes, so an
absent hook is a compile error instead of a fabricated runtime success.
The now-dead legacy runTurn->startTurn adapter (~190 LOC) is deleted with
its tests; the proxy forwards startTurn directly.
The session-reset-service call sites consolidate onto the control-plane
owner helper tryPrepareFreshManagerRuntimeSession, which now records a
visible outcome for every skipped path (backend not registered, hook not
supported) instead of returning silently; that also covers the same
silent skip in manager.close-session.
Regression tests fail pre-fix: proxy hooks reject on contract-violating
runtimes instead of fabricating success, and skipped fresh-session
preparation is recorded.
* refactor(plugin-sdk): dedupe session-catalog cursor paging into session-catalog-runtime
The acpx Pi and opencode session catalogs carried byte-identical
boundedLimit/encodeCursor/decodeCursor/optionalRawCursor/transcriptPage
scaffolding (~114 duplicated lines each). Move one canonical copy into the
private-local session-catalog-runtime SDK subpath both plugins already may
import, and keep the plugin-named cursor guards as direct aliases.
Net -87 production LOC; no behavior change (error strings, cursor canonical
form, and byte budgets are unchanged).
* refactor(core): replace private sleep() clones with canonical sleep helpers
managed-linux readiness polling now uses @openclaw/retry sleepWithAbort with
ref:false (preserving the clone's timer.unref behavior), and the embedded
agent runner's async-task wait uses src/utils/sleep.ts. Both clones matched
canonical semantics on real inputs (positive integer poll intervals).
The update-managed-service-handoff copy stays: it lives inside a serialized
standalone handoff script (String.raw template) that cannot import repo
modules.
* fix(sessions): isolated gateways no longer inherit HOME external session catalogs
A gateway on isolated state (custom OPENCLAW_STATE_DIR/CONFIG_PATH/OPENCLAW_HOME,
relocated home, or any named profile) listed, read, continued, archived, and
reopened the operator's real Claude Code/Codex/OpenCode/Pi sessions from the
process HOME. External catalogs now require the default install identity for
process-HOME scans: every catalog verb receives the isolation policy and rejects
HOME-fallback local targets, unknown providers fail closed unless they declare
supportsProcessHomeIsolation, and one structured warning records the skip.
Paired-node hosts and explicitly rooted stores (CLAUDE_CONFIG_DIR, CODEX_HOME,
OPENCODE_DB, Pi session dirs) keep working; default-identity gateways are
unchanged.
* fix(sessions): inject catalog HOME-isolation fact at registry construction
* chore(sdk): regenerate plugin API baselines after rebase
* chore(sdk): regenerate plugin API baselines after rebase
* refactor: consolidate coercion ownership
Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries.
The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries.
* fix: guard integer option ownership
Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation.
* fix: keep integer helpers on numeric facade
Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract.
* fix: point numeric coercion to number runtime
Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
* 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>