All persistent data lives under the workspace-relative .teaagent/ directory unless noted otherwise.
.teaagent/
├── config.json # Workspace configuration
├── tui_state.json # TUI session state (last exit)
├── workspace_registry.json # Global workspace lock registry (~/.teaagent/)
├── runs/
│ └── <run_id>.jsonl # Audit log + run events (one event per line)
├── sessions/
│ └── <session_id>.json # Saved chat sessions
├── plans/
│ └── <timestamp>-<slug>.md
├── undo/
│ └── <run_id>/ # Undo journal snapshots
└── graph.db # GraphQLite database (optional)
Path: .teaagent/runs/<run_id>.jsonl
Format: JSONL (one JSON object per line, UTF-8)
Appended: Each event is appended atomically during a run.
{
"event_id": "a1b2c3d4e5f6...",
"event_type": "string",
"run_id": "run_20260602_abc123",
"created_at": "2026-06-02T10:00:00.000Z",
"payload": { ... },
"prev_hash": "hex-string-or-null",
"hash": "hex-string-or-null",
"chain_hmac": "hex-string-or-null"
}| Field | Type | Description |
|---|---|---|
event_id |
string | UUID hex, globally unique |
event_type |
string | See event type table below |
run_id |
string | Identifies the parent run |
created_at |
string | ISO 8601 with milliseconds, UTC |
payload |
object | Event-specific data (see below) |
prev_hash |
string|null | SHA-256 of previous event record (chain integrity) |
hash |
string|null | SHA-256 of this event record |
chain_hmac |
string|null | HMAC-SHA256 of this record using chain key |
| Level | Data included |
|---|---|
L0 |
event_type, run_id, created_at only (metrics-safe) |
L1 |
L0 + event_id, tool name (no arguments) |
L2 |
L1 + redacted payload (sensitive fields masked as "[REDACTED]") |
L3 |
Full unredacted record |
{
"task": "string",
"provider": "string",
"model": "string",
"permission_mode": "string",
"max_iterations": 10,
"max_tool_calls": 10,
"max_estimated_cost_cents": 0,
"git_sandbox": false,
"allow_destructive": false
}{
"status": "completed | failed | approval_denied | limit_exceeded | cost_exceeded",
"iterations": 3,
"tool_calls": 7,
"cost_cents": 0.042,
"input_tokens": 4200,
"output_tokens": 1800,
"error_message": "string | null",
"final_answer": "string | null"
}{
"tool_name": "write_file",
"call_id": "call_abc123",
"arguments": { "path": "src/auth.py", "content": "[REDACTED at L2]" },
"reasoning": "string | null",
"annotations": {
"read_only": false,
"destructive": true,
"security_tier": "High"
}
}{
"tool_name": "write_file",
"call_id": "call_abc123",
"result": { "success": true, "bytes_written": 4096 },
"elapsed_ms": 23
}{
"tool_name": "write_file",
"call_id": "call_abc123",
"error_type": "PermissionError",
"error_message": "Permission denied: src/auth.py"
}{
"call_id": "call_abc123",
"tool_name": "shell_exec",
"arguments": { "command": "rm -rf dist/" },
"reason": "Destructive tool requires approval",
"annotations": { "destructive": true, "security_tier": "High" }
}{
"call_id": "call_abc123",
"tool_name": "shell_exec",
"granted_by": "jit_prompt | preset | multi_sig | pre_approved",
"reason": "string | null"
}{
"provider": "claude",
"model": "claude-opus-4-8",
"input_tokens": 4200,
"tool_count": 3,
"iteration": 2
}{
"stop_reason": "tool_use | end_turn | max_tokens",
"output_tokens": 450,
"cost_cents": 0.011,
"tool_calls_requested": 2
}{
"cumulative_cost_cents": 0.034,
"cumulative_input_tokens": 8400,
"cumulative_output_tokens": 2700,
"cap_cents": 100
}Path: .teaagent/sessions/<session_id>.json
{
"$schema": "https://teaagent.dev/schemas/session/v1.json",
"id": "abc123",
"created_at": "2026-06-01T09:14:00Z",
"updated_at": "2026-06-02T10:22:00Z",
"label": "auth refactor",
"messages": [
{
"role": "user | assistant | system",
"content": "string"
}
],
"focus_stack": {
"frames": [
{
"topic": "string",
"state": "created | kept | pushed | returned | cleared",
"message_start_index": 0,
"message_end_index": 12,
"conclusion": "string | null"
}
]
}
}| Field | Type | Description |
|---|---|---|
id |
string | Unique session identifier |
created_at |
string | ISO 8601, UTC |
updated_at |
string | ISO 8601, updated on every save |
label |
string | Human-readable label (may be empty) |
messages |
array | Ordered message history |
focus_stack.frames |
array | Context focus stack entries |
Path: .teaagent/config.json
{
"llm": {
"provider": "claude",
"model": "claude-opus-4-8",
"base_url": "https://api.anthropic.com"
},
"approval": {
"mode": "prompt",
"presets": {
"my-preset": [
{
"tool": "read_file",
"allow": true
},
{
"tool": "write_file",
"allow": false,
"reason": "writes require review"
}
]
}
},
"multi_sig": {
"enabled": false,
"required_approvals": 2,
"peer_agent_ids": ["peer-a", "peer-b"],
"peer_public_keys": {
"peer-a": "ssh-ed25519 AAAA..."
},
"peer_relay_urls": {
"peer-a": "https://relay.example.com"
},
"local_relay_base_url": null,
"allow_dev_signatures": false,
"high_risk_patterns": ["shell_exec", "rm_*"],
"timeout_seconds": 300
},
"permissions": {
"mode": "workspace-write"
},
"telemetry": {
"otlp_endpoint": null,
"service_name": "teaagent",
"console": false
},
"profiles": {
"ci": {
"llm": { "provider": "gpt" },
"approval": { "mode": "allow" }
}
}
}Profile values override top-level values when --profile ci is passed.
Path: .teaagent/tui_state.json
{
"provider": "claude",
"model": null,
"root": "/Users/me/project",
"allow_destructive": false,
"permission_mode": "prompt",
"progress": true,
"stream": false,
"subagent": false,
"chat": false,
"session_id": "abc123",
"pinned_files": ["src/auth.py"],
"heartbeat": 0,
"effort": "normal",
"route_model_enabled": false
}Cost data is extracted from audit logs. The CostTracker class aggregates across all .jsonl files in .teaagent/runs/.
Per-run summary record (in memory):
{
"run_id": "run_20260602_abc123",
"label": "auth refactor",
"model": "claude-opus-4-8",
"created_at": "2026-06-02T10:00:00Z",
"status": "completed",
"cost_cents": 4.2,
"input_tokens": 12000,
"output_tokens": 4000
}When a run is suspended to background (via background REPL command or --background flag), a checkpoint is written:
Path: .teaagent/runs/<run_id>/checkpoint.json
{
"run_id": "run_20260602_abc123",
"suspended_at": "2026-06-02T10:45:00Z",
"iteration": 3,
"tool_calls_made": 5,
"messages": [ ... ],
"pending_tool_request": {
"tool_name": "shell_exec",
"arguments": { "command": "make test" },
"call_id": "call_def456",
"reasoning": null
},
"cost_cents_so_far": 1.8,
"input_tokens_so_far": 6000,
"output_tokens_so_far": 1200
}pending_tool_request is non-null only when the run was suspended mid-approval (the tool call had not yet been dispatched).
Path: ~/.teaagent/workspace_registry.json
(Global — shared across all workspaces on the machine.)
{
"/abs/path/to/project-a": {
"workspace_path": "/abs/path/to/project-a",
"owner_pid": 12345,
"acquired_at": "2026-06-02T10:00:00Z"
},
"/abs/path/to/project-b": {
"workspace_path": "/abs/path/to/project-b",
"owner_pid": 67890,
"acquired_at": "2026-06-02T11:00:00Z"
}
}Path: .teaagent/plans/<timestamp>-<slug>.md
Format: Markdown with YAML front matter.
---
task: "migrate SQLite to PostgreSQL"
created_at: "2026-06-02T10:00:00Z"
provider: "claude"
model: "claude-opus-4-8"
status: "draft"
---
# Plan: migrate SQLite to PostgreSQL
## Steps
1. Audit current schema in `models.py`
2. Add `asyncpg` dependency
3. ...Path: .teaagent/undo/<run_id>/
Each file modified during a run has a snapshot:
.teaagent/undo/run_abc123/
├── manifest.json # List of all modified files
└── snapshots/
├── <encoded_path> # Pre-modification file content
└── ...
manifest.json:
{
"run_id": "run_abc123",
"created_at": "2026-06-02T10:00:00Z",
"files": [
{
"path": "src/auth.py",
"snapshot_key": "c2F2ZWQ...",
"existed_before": true,
"size_before": 4096
}
]
}