| name | codex-session-manager |
| description | Monitor and manage OpenAI Codex CLI sessions. List history, view session content, resume via ACP, track statistics. Use when user asks about Codex sessions, wants to check progress, or resume previous work. |
| metadata | {"openclaw":{"emoji":"📜","requires":{"anyBins":["codex"]}}} |
Codex Session Manager
Manage OpenAI Codex CLI sessions from OpenClaw.
Session Storage
Codex sessions are stored in ~/.codex/sessions/:
~/.codex/sessions/
└── 2026/
└── 04/
├── 04/
│ └── rollout-2026-04-04T22-43-49-xxx.jsonl
├── 05/
│ └── rollout-2026-04-05T08-31-04-xxx.jsonl
└── ...
Each .jsonl file contains the full transcript of a Codex session.
Commands
List Sessions
find ~/.codex/sessions -type f -name "*.jsonl" | sort -r | head -20
find ~/.codex/sessions/2026/04/09 -type f -name "*.jsonl"
View Session Summary
python3 << 'EOF'
import json
session_file = "/path/to/session.jsonl"
messages = []
with open(session_file) as f:
for line in f:
msg = json.loads(line)
messages.append(msg)
print(f"Session: {session_file.split('/')[-1]}")
print(f"Total messages: {len(messages)}")
print(f"Messages breakdown:")
for msg in messages[:5]:
role = msg.get('role', 'unknown')
content_preview = str(msg.get('content', ''))[:100]
print(f" [{role}] {content_preview}...")
EOF
Session Statistics
python3 << 'EOF'
import json
import os
from pathlib import Path
from datetime import datetime
sessions_dir = Path.home() / ".codex" / "sessions"
stats = {
'total': 0,
'by_date': {},
'avg_messages': 0,
'message_counts': []
}
for jsonl in sessions_dir.rglob("*.jsonl"):
try:
with open(jsonl) as f:
count = sum(1 for _ in f)
stats['total'] += 1
stats['message_counts'].append(count)
filename = jsonl.name
date_str = filename.split('T')[0].replace('rollout-', '')
stats['by_date'][date_str] = stats['by_date'].get(date_str, 0) + 1
except:
pass
if stats['message_counts']:
stats['avg_messages'] = sum(stats['message_counts']) / len(stats['message_counts'])
print(f"Total sessions: {stats['total']}")
print(f"Average messages: {stats['avg_messages']:.1f}")
print(f"Sessions by date:")
for date, count in sorted(stats['by_date'].items(), reverse=True)[:10]:
print(f" {date}: {count} sessions")
EOF
Find Session by Content
grep -r "your keyword" ~/.codex/sessions/*.jsonl | head -10
Resume Session via ACP
Use sessions_spawn with resumeSessionId to continue a Codex session:
{
"runtime": "acp",
"agentId": "codex",
"resumeSessionId": "<session-uuid-from-jsonl>",
"task": "Continue from where we left off..."
}
Extract Session UUID
python3 << 'EOF'
import json
session_file = "/path/to/session.jsonl"
with open(session_file) as f:
first_msg = json.loads(f.readline())
session_id = first_msg.get('session_id', 'unknown')
print(f"Session UUID: {session_id}")
EOF
Integration with OpenClaw
From Chat Command
When user asks:
- "show me Codex sessions" → list sessions
- "what was in session X" → view summary
- "resume Codex session" → ACP spawn with resumeSessionId
- "Codex statistics" → aggregate stats
ACP Resume Pattern
sessions = find_codex_sessions(date="2026-04-09")
session_id = extract_session_uuid(sessions[0])
sessions_spawn(
runtime="acp",
agentId="codex",
resumeSessionId=session_id,
task="Continue the previous task"
)
Session JSONL Format
Codex uses a structured event format:
{
"timestamp": "2026-04-07T14:14:09.273Z",
"type": "session_meta|event_msg|response_item",
"payload": {
}
}
Event Types
| Type | Description | Key Payload Fields |
|---|
session_meta | Session initialization | id (session UUID), cwd, cli_version, model_provider, git.branch, git.commit_hash |
event_msg | Events like task_started | type (event type), turn_id |
response_item | Messages | type ("message"), role ("developer" |
Extract Session Info
import json
from pathlib import Path
def parse_codex_session(jsonl_path):
"""Parse Codex session and extract key info."""
with open(jsonl_path) as f:
lines = [json.loads(line) for line in f]
meta = next((l for l in lines if l.get('type') == 'session_meta'), None)
messages = [l for l in lines if l.get('type') == 'response_item']
roles = {}
for msg in messages:
role = msg.get('payload', {}).get('role', 'unknown')
roles[role] = roles.get(role, 0) + 1
return {
'session_id': meta.get('payload', {}).get('id', 'unknown') if meta else 'unknown',
'cwd': meta.get('payload', {}).get('cwd', 'unknown') if meta else 'unknown',
'branch': meta.get('payload', {}).get('git', {}).get('branch', 'unknown') if meta else 'unknown',
'cli_version': meta.get('payload', {}).get('cli_version', 'unknown') if meta else 'unknown',
'total_events': len(lines),
'message_count': len(messages),
'roles': roles,
'filename': jsonl_path.name,
'date': jsonl_path.name.split('T')[0].replace('rollout-', ''),
'time': jsonl_path.name.split('T')[1].split('-')[0] if 'T' in jsonl_path.name else ''
}
Monitoring Running Sessions
Check if Codex is currently running:
ps aux | grep codex | grep -v grep
process action:list
Workflow
- List → Find relevant session
- View → Understand what happened
- Resume → Continue work via ACP
- Monitor → Track progress with process tool
Related: coding-agent skill, acp-router skill