`runtime-options.buildRuntimeConfigOptionPairs` translated
`AcpSessionRuntimeOptions.timeoutSeconds` into a
`session/set_config_option(configId: "timeout")` pair on every turn. Both the
control plane (`AcpSessionManager.applyManagerRuntimeControls`) and the ACPX
wrapper (`AcpxRuntime.setConfigOption`) sit between that pair and the backend:
- The control plane validates pairs against the backend's advertised
config-option keys and throws `ACP_BACKEND_UNSUPPORTED_CONTROL` for any
pair the backend did not advertise. claude-agent-acp does not advertise a
`timeout` alias.
- The wrapper then forwards remaining pairs to the delegate. The Codex ACP
command was already short-circuited there; every other command, including
claude-agent-acp, fell through.
Net effect on the reporter's scenario:
`sessions_spawn({ runtime:"acp", agentId:"claude", timeoutSeconds: 60 })`
failed at the control-plane validation with `ACP_BACKEND_UNSUPPORTED_CONTROL`
(and, had it reached the wire, claude-agent-acp would have answered
`-32603 Internal error / Unknown config option: timeout`, surfacing as
`ACP_TURN_FAILED: Internal error`).
Fix two layers:
1. Control plane (`src/acp/control-plane/runtime-options.ts`): add
`isTimeoutConfigOptionAdvertised(advertisedConfigOptionKeys)` and gate the
timeout pair on it. When advertised keys are unknown (`undefined` or
empty), keep emitting the pair — this preserves current behavior for
backends that have not produced a capability list yet. When advertised
keys are present but exclude every alias in
`RUNTIME_CONFIG_OPTION_ALIASES.timeoutSeconds`, skip the pair. The
per-turn timeout is still enforced in-process via
`AcpSessionManager.resolveTurnTimeoutMs` in `manager.core.ts`.
2. ACPX wrapper (`extensions/acpx/src/runtime.ts`): hoist the Codex
`timeout` / `timeout_seconds` suppression so it also applies to
claude-agent-acp commands. Add `isClaudeAcpCommand` mirroring
`isCodexAcpCommand` (package spec, binary, generated wrapper script).
This layer is defense in depth — relevant when callers reach the wrapper
without going through `applyManagerRuntimeControls`, or when advertised
keys are not yet known.
Coverage:
- `src/acp/control-plane/runtime-options.test.ts` (new) asserts:
- the timeout pair is omitted when advertised keys exclude every alias,
- the pair is kept when `timeout` or `timeout_seconds` is advertised,
- the pair is kept when advertised keys are unknown,
- model/thinking emission is unaffected.
- `extensions/acpx/src/runtime.test.ts` flips the previous
`forwards timeout config controls for non-Codex ACP agents` test, which
codified the buggy behavior, into a suppression assertion. Adds a
positive `still forwards non-timeout config controls for claude-agent-acp`
test and an `isClaudeAcpCommand` detector test.
Closes#81127
* fix(acpx): store agent session ID when session/load fails
When an ACP agent (e.g. Gemini CLI) rejects the acpx-generated session
ID via session/load and falls back to session/new, the agent-returned
session ID was previously discarded. This caused identity stuck at
pending forever, multi-turn failures, lost completion events, and
persistent reconcile warnings.
- Parse ACP protocol stream in runTurn() to capture agent session IDs
- Flip resolveRuntimeResumeSessionId() to prefer agentSessionId
- Add createIdentityFromHandleEvent() for handle-sourced identity
- Layer handle event identity before status in reconcile
- Add regression tests for load fallback and restart resume
Closes#52182
* ACPX: prefer decoded session ids
* ACPX: refresh runtime handle state from status
---------
Co-authored-by: Wesley <imwyvern@users.noreply.github.com>
* feat(acp): add resumeSessionId to sessions_spawn for ACP session resume
Thread resumeSessionId through the ACP session spawn pipeline so agents
can resume existing sessions (e.g. a prior Codex conversation) instead
of starting fresh.
Flow: sessions_spawn tool → spawnAcpDirect → initializeSession →
ensureSession → acpx --resume-session flag → agent session/load
- Add resumeSessionId param to sessions-spawn-tool schema with
description so agents can discover and use it
- Thread through SpawnAcpParams → AcpInitializeSessionInput →
AcpRuntimeEnsureInput → acpx extension runtime
- Pass as --resume-session flag to acpx CLI
- Error hard (exit 4) on non-existent session, no silent fallback
- All new fields optional for backward compatibility
Depends on acpx >= 0.1.16 (openclaw/acpx#85, merged, pending release).
Tests: 26/26 pass (runtime + tool schema)
Verified e2e: Discord → sessions_spawn(resumeSessionId) → Codex
resumed session and recalled stored secret.
🤖 AI-assisted
* fix: guard resumeSessionId against non-ACP runtime
Add early-return error when resumeSessionId is passed without
runtime="acp" (mirrors existing streamTo guard). Without this,
the parameter is silently ignored and the agent gets a fresh
session instead of resuming.
Also update schema description to note the runtime=acp requirement.
Addresses Greptile review feedback.
* ACP: add changelog entry for session resume (#41847) (thanks @pejmanjohn)
---------
Co-authored-by: Pejman Pour-Moezzi <481729+pejmanjohn@users.noreply.github.com>
Co-authored-by: Onur <onur@textcortex.com>