Date: 2026-02-21
This captures code changes made in picoclaw around agent loop behavior and related parity work, plus a short status note on transport parity.
pkg/agent/loop.gopkg/agent/context.godocs/review/agent-loop-vs-codex-2026-02-20.md
Runnow routes each session through a dedicated worker with a bounded queue.processSessionMessagereceives the queue and buildsGetPendingInputcallback to drain queued follow-up input before each iteration.processMessagenow acceptsGetPendingInputand forwards it for normal and system/system-like message flows.
- Added
collectPendingInput,latestPendingContent, andlatestPendingMedia. runTurnnow uses latest queued item as turn input on iteration 1 (text and/or media), while later iterations use follow-up continuation mode (`iterationInput="").buildTurnMessagesnow builds message payload each iteration from current session history and the current turn context, rather than reusing stale request text.
- Added pre-sampling compaction path with failure propagation:
compactContextForIterationautoCompactLimitFromUsage
- Added post-sampling follow-up usage checkpoint:
estimateCurrentUsageshouldCompactForFollowUp
- Deferred tool execution/turn completion when follow-up compaction is needed.
- Split into explicit loop + turn execution stages:
submissionLoop(iteration controller)runTurn(single turn pass)runSamplingRequest+tryRunSamplingRequest
- Added bounded retry loop for sampling with exponential-ish jitter (
maxSamplingRetries = 8). - Added stricter retryability filtering (
isSamplingErrorRetryable,isClientError) so client errors (bad request/invalid model/unauthorized/etc.) skip retries.
executeToolCallsnow returns(messages, toolErrorCount).runAgentLoopnow includes tool error count in empty-response fallback message.- On hard turn failure, loop stores explicit assistant fallback content and persists session state.
pkg/agent/context.goavoids appending duplicate user turns when history already includes an identical final user turn.
flowchart TD
A[Run receives inbound message] --> B[resolve session key]
B --> C[enqueue session worker queue]
C --> D[processSessionMessage]
D --> E{Queue has pending item}
E -->|yes| F[collect pending input]
E -->|no| G[processMessage]
F --> G
G --> H[runAgentLoop]
H --> I[save user message + build tool defs]
I --> J[submissionLoop iteration loop]
J --> K[runTurn]
K --> L[collectPendingInput]
K --> M[buildTurnMessages from session history]
M --> N[compact context for sampling if needed]
N --> O[runSamplingRequest]
O --> P[callLLMWithModelFallback]
P --> Q[tryRunSamplingRequest]
Q --> R{tool calls requested}
R -->|no| S[LLM response]
R -->|yes| T[executeToolCalls]
T --> U[set needsFollowUp]
U --> V[collect pending input]
V -->|yes| J
V -->|no| W[persist final response]
S --> W
flowchart TD
A[tryRunSamplingRequest] --> B[callLLMWithModelFallback]
B --> C{provider success?}
C -->|yes| D[parse assistant + tool calls]
C -->|no| E[runSamplingRequest retry gate]
E --> F{retryable error and attempts < max}
F -->|no| G[return error]
F -->|yes| H[emit retry progress + backoff]
H --> E
D --> I{needs follow up?}
I -->|no| J[recompute post-usage]
I -->|yes| K[execute tools]
J --> L{utilization >= hard ceiling}
L -->|yes| M[compact + continue]
L -->|no| N[finalize response]
- Codex does websocket preference + HTTP fallback at the client/session transport state level.
picoclawprovider interface is currently HTTP-only (/chat/completions) viaproviders.HTTPProvider.model fallbackexists (primary -> configured model fallbacks), but websocket\u2013HTTPS transport fallback does not.- This is intentionally documented as a blocker for full transport parity until a protocol transport abstraction is introduced.
- Transport/session transport-state parity (websocket pre-warm/reconnect/fallback).
- Exact token/compaction policy parity with Codex
auto_compact_token_limitbehavior. - Multi-source pending-input semantics (Codex async pending-input channel vs current worker-local queue model).