Skip to content

Instantly share code, notes, and snippets.

@euforic
Last active February 21, 2026 03:03
Show Gist options
  • Select an option

  • Save euforic/12e286f3abb3a87940ca0e4dd38021df to your computer and use it in GitHub Desktop.

Select an option

Save euforic/12e286f3abb3a87940ca0e4dd38021df to your computer and use it in GitHub Desktop.

Agent Loop Parity Update (vs /tmp/codex)

Date: 2026-02-21

Scope of this update

This captures code changes made in picoclaw around agent loop behavior and related parity work, plus a short status note on transport parity.

Files included in this pass

  • pkg/agent/loop.go
  • pkg/agent/context.go
  • docs/review/agent-loop-vs-codex-2026-02-20.md

What changed in the agent loop

1) Session queue-aware message intake

  • Run now routes each session through a dedicated worker with a bounded queue.
  • processSessionMessage receives the queue and builds GetPendingInput callback to drain queued follow-up input before each iteration.
  • processMessage now accepts GetPendingInput and forwards it for normal and system/system-like message flows.

2) Per-turn context rebuild with pending input

  • Added collectPendingInput, latestPendingContent, and latestPendingMedia.
  • runTurn now uses latest queued item as turn input on iteration 1 (text and/or media), while later iterations use follow-up continuation mode (`iterationInput="").
  • buildTurnMessages now builds message payload each iteration from current session history and the current turn context, rather than reusing stale request text.

3) Compaction parity adjustments

  • Added pre-sampling compaction path with failure propagation:
    • compactContextForIteration
    • autoCompactLimitFromUsage
  • Added post-sampling follow-up usage checkpoint:
    • estimateCurrentUsage
    • shouldCompactForFollowUp
  • Deferred tool execution/turn completion when follow-up compaction is needed.

4) Submission and retry loop

  • 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.

5) Tool error accounting and robust empty-response handling

  • executeToolCalls now returns (messages, toolErrorCount).
  • runAgentLoop now includes tool error count in empty-response fallback message.
  • On hard turn failure, loop stores explicit assistant fallback content and persists session state.

6) User message dedupe in context builder

  • pkg/agent/context.go avoids appending duplicate user turns when history already includes an identical final user turn.

Mermaid: end-to-end submission flow

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
Loading

Mermaid: retry+compaction decision tree

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]
Loading

Current transport parity status

  • Codex does websocket preference + HTTP fallback at the client/session transport state level.
  • picoclaw provider interface is currently HTTP-only (/chat/completions) via providers.HTTPProvider.
  • model fallback exists (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.

Open gaps to watch

  1. Transport/session transport-state parity (websocket pre-warm/reconnect/fallback).
  2. Exact token/compaction policy parity with Codex auto_compact_token_limit behavior.
  3. Multi-source pending-input semantics (Codex async pending-input channel vs current worker-local queue model).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment