pi-mono Agent Loop

pi-mono Agent Loop

The core execution engine in pi-mono. Drives LLM streaming, tool calls, and event emission. OpenClaw wraps it via runEmbeddedPiAgent.

Entry points

Function Use case
agentLoop(prompts, ctx, cfg) New run — adds prompts to context, starts loop
agentLoopContinue(ctx, cfg) Retry — continues from existing context; last message must be non-assistant

Both return EventStream<AgentEvent, AgentMessage[]>.

Loop anatomy

agent_start
  outer-loop:
    inner-loop:
      turn_start
      [inject steering messages]
      streamAssistantResponse → message_start / message_update* / message_end
      if stopReason == error|aborted → turn_end + agent_end, return
      executeToolCalls → tool_execution_start/update/end + message_start/end per result
      turn_end
      [check steering messages → continue inner]
    [check getFollowUpMessages → continue outer]
agent_end

Message representation

Steering vs follow-up messages

Callback When polled Effect
getSteeringMessages() Start of run + after each turn Injects user messages between turns (mid-run interrupt)
getFollowUpMessages() After agent would naturally stop Re-enters the outer loop with new messages

This is how OpenClaw's Command Queue modes (steer, followup) feed the loop.

Tool execution pipeline

tool.prepareArguments(rawArgs)
  → validateToolArguments(schema)
  → config.beforeToolCall({…}) → block?
  → tool.execute(id, args, signal, onPartial)
  → config.afterToolCall({…, result, isError}) → rewrite?
  → ToolResultMessage emitted

Execution mode: sequential or parallel (configurable). Parallel preparation fires all non-blocked execute calls concurrently, then awaits in order.

Per-turn API key resolution

config.getApiKey(provider) is called each turn — enables support for short-lived / expiring tokens without restarting the loop.