Skip to main content

Session consumer claims

WednesdayAI gives plugins a durable, at-least-once work queue over ended sessions: each finished session is materialized as claim rows per registered consumer, and your plugin pulls, leases, and acks them through the SDK. This replaces polling the session store or hooking the volatile session_end event (an in-process subscriber absent at emit time silently misses the session forever). The pieces:
  • Manifest — declare sessionConsumers in openclaw.plugin.json.
  • SDKapi.claimSessionWork(opts) / api.ackSessionWork(claimId, result) / optional api.onSessionWorkAvailable({ effectKind }).
  • Diagnosticssession_consumer_claim events surface DLQ, backstop, and skipped-backlog decisions.
Contracts live in src/plugins/manifest.ts (SessionConsumerDeclaration) and src/plugins/types.ts (ClaimedSessionWork, SessionWorkResult), re-exported from openclaw/plugin-sdk. Design decisions: ADR 0041 (claim ledger) and ADR 0062 (seam carries sessionKey + outcome).

Manifest declaration

Your consumerId is loader-bound${manifestId}:${effectKind}[/<subCursor>] — and never caller-supplied. A plugin cannot claim work as another plugin, and unauthorized items (outside your manifest’s scopes/lanes) are skipped before they reach you.

Claiming and acking work

claimSessionWork(opts?) options: { subCursor?, laneId?, limit?, leaseMs?, effectKind? }. It returns null when no durable claims store is wired (fs-jsonl without the degraded store) or no work is available. The claim payload (ClaimedSessionWork):
ackSessionWork(claimId, result) takes { kind: "processed" } or { kind: "failed", error, retryable }. After maxAttempts (default 5) retryable failures the claim dead-letters (dlq_terminal event).

Async safety

claimSessionWork/ackSessionWork are async storage calls — never call them inside synchronous hook bodies without handling the promise. Claims hold leases (default 30 s): a claim you hold but neither ack nor release blocks re-offer until the lease expires. Batch small: claim → process → ack before claiming again, as in the pump above. Do not hold claims across user-facing request paths. What not to do:

Diagnostics events

Emitting from src/infra/diagnostic-events.ts, type session_consumer_claim:
Surface these in your plugin’s observability story — backstop_released means a session your consumer never finished was pruned anyway.

Operator-facing behaviour

Declaring sessionConsumers makes pruning obligation-gated for your lanes: sessions you have not consumed are pinned (bounded by a 30-day backstop). Document this for operators in your plugin README, and point them at Session consumer claims (admin).