phi-agent 0.2.6

phi-agent — General-purpose AI Agent framework (builder factory, renderer, config, session management)
Documentation
# Advanced Usage

Middleware, sessions, event logging, and more.

## Middleware

Middleware hooks into the agent loop before and after LLM calls:

```rust
use agent_base::{TurnFactMiddleware, TurnToolLimitMiddleware};

let builder = base_agent_builder(llm_client)
    .system_prompt(system_prompt)
    .middleware(TurnFactMiddleware::new())
    .middleware(TurnToolLimitMiddleware::from_config(&safety));
```

Built-in middleware:
- `TurnFactMiddleware` — injects facts/context at the start of each turn
- `TurnToolLimitMiddleware` — enforces `max_tool_calls_per_turn`

## Approval Handlers

Control which tool calls require human confirmation:

```rust
// Auto-approve everything (CI / automation)
use phi_agent::{AutoApprovalHandler, ApprovalMode};
builder = builder.approval_handler(Arc::new(
    AutoApprovalHandler::new(ApprovalMode::Auto)
));

// Deny all (read-only / preview mode)
builder = builder.approval_handler(Arc::new(
    AutoApprovalHandler::new(ApprovalMode::DenyAll)
));
```

For interactive CLI approval, see `CliApprovalHandler` in the phi binary.

## Session Management

Sessions persist conversation history and tool call results:

```rust
use phi_agent::session::{resolve_session, cleanup_expired_sessions};

// Create or reuse a session
let ctx = resolve_session(Some("my-session"), &base_dir)?;
println!("Session: {} (new: {})", ctx.session_id, ctx.is_new_session);

// Clean up old sessions (> 7 days)
let cleaned = cleanup_expired_sessions(&base_dir, 7)?;
println!("Cleaned {} expired sessions", cleaned);
```

Session directory layout:
```
~/.phi-agent/sessions/<id>/
├── session_id           # Session ID marker
├── session.lock         # Exclusive file lock
├── session_meta.json    # Created at, last active at
└── turn_001.jsonl       # Per-turn event log (JSONL)
```

## Event Logging

Every turn is persisted as JSONL for replay and analysis:

```rust
use phi_agent::{save_turn_log, event_to_jsonl};

// Save turn events
save_turn_log(&session_ctx, 1, &events, "user query")?;

// Convert a single event to JSONL
let line = event_to_jsonl(&event);
```

Event types in the log:
- `thought_delta` — LLM thinking content
- `text_delta` — Assistant text output
- `tool_call_started` / `tool_call_finished` — Tool invocations
- `approval_request` — When a tool needs approval
- `plan_updated` — Task plan changes
- `turn_finished` — Turn summary with duration and stats

## System Prompts

phi-agent provides two system prompt variants:

```rust
use phi_agent::{build_system_prompt, build_system_prompt_cn};

// Default (international)
let prompt = build_system_prompt();

// China-aware variant (prefers domestic services, handles GFW)
let prompt_cn = build_system_prompt_cn();
```

You can also pass a fully custom prompt via `builder.system_prompt(...)`.

## Reasoning / Thinking

Control the LLM's chain-of-thought behavior:

```rust
use agent_base::{ReasoningConfig, ReasoningEffort};

// Builder-level default
builder = builder.reasoning(ReasoningConfig {
    effort: Some(ReasoningEffort::High),
    ..Default::default()
});

// Per-turn override
agent.set_reasoning_effort(ReasoningEffort::XHigh).await;
```

Effort levels and when to use them:
- `Low` — simple tasks, fast responses
- `Medium` — default, balanced
- `High` — complex multi-step tasks
- `XHigh` — hardest problems, longest think time

## Programmatic Renderers

Use renderers outside the CLI:

```rust
use phi_agent::{
    TerminalRenderer, JsonStreamRenderer, NullRenderer, EventRenderer,
};
use std::io;

// Terminal
let mut renderer = TerminalRenderer::new(true, true, true, Box::new(io::stdout()));

// JSON stream (for IDE integration)
let mut renderer = JsonStreamRenderer::stdout();

// Silent (for web backends)
let mut renderer = NullRenderer;
```

## Error Recovery

phi-agent configures consecutive failure recovery by default:

```rust
use agent_base::ConsecutiveFailureRecovery;

// 3 consecutive failures → stop and explain
builder = builder.error_recovery(Arc::new(
    ConsecutiveFailureRecovery::new(3)
));
```