Metadata-Version: 2.4
Name: opentime
Version: 0.1.0
Summary: Temporal awareness and time-effort estimation for AI agents
Project-URL: Homepage, https://github.com/transparentai-tech/OpenTime
Project-URL: Repository, https://github.com/transparentai-tech/OpenTime
Project-URL: Issues, https://github.com/transparentai-tech/OpenTime/issues
Author: ian-c-hamilton
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,mcp,temporal,time
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.11
Requires-Dist: mcp>=1.0.0
Provides-Extra: all
Requires-Dist: fastapi>=0.100.0; extra == 'all'
Requires-Dist: httpx>=0.27; extra == 'all'
Requires-Dist: mypy>=1.10; extra == 'all'
Requires-Dist: pytest-asyncio>=0.23; extra == 'all'
Requires-Dist: pytest>=8.0; extra == 'all'
Requires-Dist: ruff>=0.4; extra == 'all'
Requires-Dist: uvicorn>=0.20.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: rest
Requires-Dist: fastapi>=0.100.0; extra == 'rest'
Requires-Dist: uvicorn>=0.20.0; extra == 'rest'
Description-Content-Type: text/markdown

# OpenTime

**Temporal awareness and time-effort estimation for AI agents.**

[![PyPI version](https://badge.fury.io/py/opentime.svg)](https://pypi.org/project/opentime/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/downloads/)

---

## The Problem

AI agents have no concept of time. Their time intuition comes from LLM training data calibrated to **human speed**, not agent speed. This causes real problems:

- **Bad timeouts**: An agent sets a 60-minute timeout on a download that takes 65 minutes. It times out at 98% completion, losing all progress and doubling the total time.
- **Wrong decisions**: An agent chooses a 23-hour approach over a 9-hour approach because it estimates coding time at human speed — but the coding portion takes minutes, not hours, for an agent.
- **No self-awareness**: An agent has no idea how long it actually takes to complete tasks, how long it's been running, or when the last user interaction was.

## The Solution

OpenTime gives any AI agent the ability to:

- **Track time** — wall clock, elapsed time, stopwatches
- **Record events** — task start/end with automatic correlation IDs for overlapping tasks
- **Learn duration estimates** — per-agent, per-task-type statistics (mean, median, p95)
- **Get timeout recommendations** — "Based on your history, set a 45-second timeout for this task"
- **Compare approaches** — "The 'hard way' actually saves 17 hours given your coding speed"
- **Passive tracking** — automatically record tool usage durations via hooks (no agent action needed)

Each agent builds its own time-task database over time, learning its actual capabilities rather than relying on human-calibrated estimates.

## Installation

```bash
pip install opentime
```

With REST API support:
```bash
pip install opentime[rest]
```

## Integration Options

OpenTime works with any AI agent through three interfaces:

| Interface | Best For | Requires Agent Awareness? |
|-----------|----------|--------------------------|
| **MCP Server** | Claude Code, Claude Desktop, Cursor, Windsurf, Cline | Yes (agent calls tools) |
| **REST API** | ChatGPT, Gemini, LangChain, custom agents | Yes (agent calls endpoints) |
| **Hooks** | Claude Code passive tracking | No (fully automatic) |

### Option 1: MCP Server

For any MCP-compatible client (Claude Code, Claude Desktop, Cursor, Windsurf, etc.):

```json
{
  "mcpServers": {
    "opentime": {
      "command": "opentime-mcp",
      "env": {
        "OPENTIME_DB_PATH": "~/.opentime/agent.db",
        "OPENTIME_AGENT_ID": "my-agent"
      }
    }
  }
}
```

The agent gets access to 21 tools for time tracking, event recording, and duration statistics.

### Option 2: REST API

For any agent that can make HTTP calls — ChatGPT custom actions, Gemini function calling, LangChain tools, AutoGPT, CrewAI, or your own agents:

```bash
# Start the server
opentime-rest
# Or with custom settings
OPENTIME_DB_PATH=~/.opentime/agent.db OPENTIME_AGENT_ID=my-agent opentime-rest
```

The API runs at `http://127.0.0.1:8080` with interactive docs at `/docs` (Swagger UI).

**Example: Record a task and get stats**

```bash
# Start a task
curl -X POST http://localhost:8080/events/task-start \
  -H "Content-Type: application/json" \
  -d '{"task_type": "code_generation"}'
# Returns: {"event": {...}, "correlation_id": "abc123..."}

# End the task (pass the correlation_id back)
curl -X POST http://localhost:8080/events/task-end \
  -H "Content-Type: application/json" \
  -d '{"task_type": "code_generation", "correlation_id": "abc123..."}'

# Get duration statistics
curl http://localhost:8080/stats/durations/code_generation
# Returns: {"summary": {"count": 15, "mean_seconds": 8.2, "median_seconds": 7.5, ...}}

# Get a timeout recommendation
curl http://localhost:8080/stats/recommend-timeout/code_generation
# Returns: {"recommendation": {"recommended_seconds": 18.6, "percentile": 0.95, ...}}
```

### Option 3: Passive Hooks (Claude Code)

For fully automatic time tracking with zero agent involvement:

```json
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "",
      "hooks": [{"type": "command", "command": "python -m opentime.hooks.claude_code"}]
    }],
    "PostToolUse": [{
      "matcher": "",
      "hooks": [{"type": "command", "command": "python -m opentime.hooks.claude_code"}]
    }],
    "Stop": [{
      "hooks": [{"type": "command", "command": "python -m opentime.hooks.claude_code"}]
    }]
  }
}
```

Every tool call is automatically timed and recorded. Query the data later:

```python
from opentime.db.connection import open_database
from opentime.core.stats import DurationStats

conn = open_database("~/.opentime/claude-code.db")
stats = DurationStats(conn, "claude-code")

# How long do Bash commands take?
print(stats.summarize("tool:Bash"))

# How long do file edits take?
print(stats.summarize("tool:Edit"))
```

## MCP Tools Reference

**Clock** (2 tools)
| Tool | Description |
|------|-------------|
| `clock_now` | Current UTC time as ISO 8601 |
| `clock_elapsed_since` | Seconds elapsed since a timestamp |

**Stopwatch** (5 tools)
| Tool | Description |
|------|-------------|
| `stopwatch_start` | Start a named stopwatch |
| `stopwatch_read` | Read elapsed time without stopping |
| `stopwatch_stop` | Stop and get final elapsed time |
| `stopwatch_list` | List all stopwatches |
| `stopwatch_delete` | Delete a stopwatch |

**Events** (6 tools)
| Tool | Description |
|------|-------------|
| `event_record` | Record a generic timestamped event |
| `event_task_start` | Start a task (returns correlation_id) |
| `event_task_end` | End a task (pass correlation_id to pair) |
| `event_list` | Query events with filters |
| `event_get` | Get a single event by ID |
| `event_active_tasks` | List started-but-not-ended tasks |

**Statistics** (6 tools)
| Tool | Description |
|------|-------------|
| `stats_duration` | Duration stats (mean, median, p95) for a task type |
| `stats_list_task_types` | List all task types with data |
| `stats_all` | Stats for all task types |
| `stats_recommend_timeout` | Recommend a timeout based on historical durations |
| `stats_check_timeout` | Check if a running task is at risk of timeout |
| `stats_compare_approaches` | Compare approaches using actual historical speed |

## REST API Endpoints

All MCP tools are mirrored as REST endpoints:

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/health` | Health check |
| GET | `/clock/now` | Current time |
| GET | `/clock/elapsed?since=` | Elapsed time |
| POST | `/stopwatch/{name}/start` | Start stopwatch |
| GET | `/stopwatch/{name}` | Read stopwatch |
| POST | `/stopwatch/{name}/stop` | Stop stopwatch |
| GET | `/stopwatches` | List stopwatches |
| DELETE | `/stopwatch/{name}` | Delete stopwatch |
| POST | `/events` | Record event |
| POST | `/events/task-start` | Start task |
| POST | `/events/task-end` | End task |
| GET | `/events/active` | Active tasks |
| GET | `/events` | List events |
| GET | `/events/{id}` | Get event |
| GET | `/stats/durations/{task_type}` | Duration stats |
| GET | `/stats/task-types` | List task types |
| GET | `/stats/durations` | All stats |
| GET | `/stats/recommend-timeout/{task_type}` | Timeout recommendation |
| GET | `/stats/check-timeout/{task_type}` | Timeout risk check |
| POST | `/stats/compare-approaches` | Compare approaches |

## Configuration

OpenTime is configured via environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `OPENTIME_DB_PATH` | `opentime.db` | Path to the SQLite database file |
| `OPENTIME_AGENT_ID` | `default` | Unique identifier for this agent |
| `OPENTIME_HOST` | `127.0.0.1` | REST API host |
| `OPENTIME_PORT` | `8080` | REST API port |

## Architecture

```
MCP Server / REST API        <- thin wrappers, no business logic
        |
        v
+------------------------+
|   ClockService         |  <- stateless, in-memory stopwatches
|   EventTracker         |--> db.queries --> SQLite (per-agent)
|   DurationStats        |--> db.queries --> SQLite (per-agent)
+------------------------+
```

- **Core layer** (`opentime.core`) — all business logic, fully decoupled from transport
- **DB layer** (`opentime.db`) — SQLite schema, connections, migrations, parameterized queries
- **MCP server** (`opentime.mcp_server`) — FastMCP tool registrations with lifespan-managed state
- **REST API** (`opentime.rest_api`) — FastAPI endpoints mirroring MCP tools
- **Hooks** (`opentime.hooks`) — passive tracking integrations (Claude Code)

## Development

```bash
# Setup
git clone https://github.com/transparentai-tech/OpenTime.git
cd OpenTime
python3 -m venv .venv && .venv/bin/pip install -e ".[all]"

# Tests
.venv/bin/pytest -v                    # 109 tests

# Lint
.venv/bin/ruff check src/ tests/
```

## License

MIT
