v0.2: multi-turn /sessions endpoints backed by ACPX
- Dockerfile: install acpx@latest alongside @anthropic-ai/claude-code
- compose.yml: bind /mnt/user/appdata/clawdforge/acpx-sessions:/root/.acpx/sessions
- DB: additive sessions + session_events tables in store.py SCHEMA
- clawdforge/acpx_runner.py: AcpxManager + AcpxSession, bounded async pool,
per-invocation subprocess model (acpx CLI itself owns the queue-owner
lifecycle, so each turn = one fresh `acpx prompt -s <uuid>` call)
- server.py: POST/GET/DELETE /sessions + POST /sessions/{id}/turn + GET /sessions
- Per-app isolation: 404 (not 403) on cross-token session access — no
existence leak across tokens
- Lifespan-managed TTL sweeper: every 60s soft-closes idle sessions past
CLAWDFORGE_SESSION_TTL_SECS (1h default), hard-deletes ledger rows past
CLAWDFORGE_SESSION_HARD_TTL_SECS (24h default)
- session_events audit table parallel to existing runs table
(events: create, turn, close, sweep_close, hard_delete)
- /healthz now reports acpx_present + acpx_version + open_sessions count
- tests/test_sessions.py: 16 tests covering create/turn/close/list/isolation/
sweep/pool-full/regression. /run regression test asserts byte-identical
v0.1 response shape.
ACPX research notes (v0.6.1, openclaw/acpx):
- npm package is `acpx`, not `@openclaw/acpx`
- Sessions are scoped by (agentCommand, cwd, name?). We mint our own UUID
as `--name` and give every session a unique cwd subdir, so the scope key
is collision-free across apps.
- session_id source: ours. We pass --name <uuid>, ACPX records it under
~/.acpx/sessions/<encoded-id>.json. We never need to parse ACPX's
acpxRecordId — our UUID is canonical.
- Subprocess lifetime: per-invocation, NOT per-session. The acpx CLI itself
spawns/maintains a per-session "queue owner" process via local IPC; each
`acpx prompt` call we make either elects itself owner or enqueues. The
AcpxSession class is therefore a thin (uuid, cwd, asyncio.Lock) handle,
not a long-lived stdio pipe. The spec's "owns one stdio pipe pair" model
was rewritten to match reality — flagged here.
- Close semantics: soft-close via `acpx sessions close <name>`. The
on-disk record stays (ACPX's `sessions prune` is the hard-delete path,
not invoked from clawdforge). DELETE /sessions/<id> is documented as
idempotent (200 with already_closed=true on second call) so SDKs can
call close() in finally/Drop blocks safely.
- File uploads: ACPX has no file-attach ACP method exposed via the CLI.
We prepend a [Attached files] header listing absolute paths; the agent
uses its Read tool to open them. Same behavior as /run --files in v0.1.
- Permissions: --approve-all on the turn invocation since the container is
unattended and callers are bearer-token-trusted. Future v0.3 may expose
a per-session permission policy.
/run endpoint unchanged — backwards compat verified by
test_run_endpoint_unchanged + test_run_endpoint_unchanged_error_shape.
Spec: memory/spec-clawdforge-v0.2.md
ACPX CLI ref: https://github.com/openclaw/acpx/blob/main/docs/CLI.md
This commit is contained in:
parent
19fe299b3d
commit
940861f70a
11 changed files with 1829 additions and 20 deletions
14
.env.example
14
.env.example
|
|
@ -22,6 +22,20 @@ CLAUDE_BIN=claude
|
|||
DEFAULT_MODEL=sonnet
|
||||
DEFAULT_TIMEOUT_SECS=120
|
||||
|
||||
# ACPX (multi-turn /sessions endpoints). Reuses Claude Code auth at /root/.claude.
|
||||
ACPX_BIN=acpx
|
||||
# Working directory for each session's CWD (acpx scopes by cwd; we give each session its own subdir).
|
||||
ACPX_SESSIONS_CWD=/data/acpx-cwds
|
||||
# Max simultaneously-open (non-closed) sessions across all apps. New /sessions returns 503 if at cap.
|
||||
CLAWDFORGE_MAX_LIVE_SESSIONS=32
|
||||
# How long an idle session lives before the sweeper soft-closes it. Counted from last_turn_at (or
|
||||
# created_at if no turn ever ran).
|
||||
CLAWDFORGE_SESSION_TTL_SECS=3600
|
||||
# How long a closed session record stays before hard-delete (ledger row + acpx on-disk metadata).
|
||||
CLAWDFORGE_SESSION_HARD_TTL_SECS=86400
|
||||
# Sweep cadence in seconds.
|
||||
CLAWDFORGE_SWEEP_INTERVAL_SECS=60
|
||||
|
||||
# Run-staging area inside the container (don't change unless you also change compose mount)
|
||||
RUNS_DIR=/data/runs
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue