Metadata-Version: 2.4
Name: pysince
Version: 0.2.0
Summary: Temporal context for LLM conversations — time awareness + stale-context detection
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# pysince

**Temporal context for LLMs — time awareness + stale-context detection, zero AI calls.**

```
pip install pysince
from since import Store, since_time
```

## What it does

LLMs have no sense of time. Ask "what time is it?" and they guess. Ask "what did we talk about 3 hours ago?" and they can't. Context from 5 minutes ago looks the same as context from yesterday.

`pysince` (imported as `since`) wraps your LLM calls with a real clock, persistent timestamped memory, and TTL-based staleness detection.

## Quick start

```python
from since import Store, since_time
from openai import OpenAI

store = Store("~/.since/chat.db")
client = OpenAI()

@since_time(store=store, timezone="UTC+05:30")
def chat(messages):
    return client.chat.completions.create(model="gpt-4o", messages=messages)

# Every message is timestamped automatically
resp = chat(messages=[{"role": "user", "content": "hello"}])
print(resp.choices[0].message.content)
```

The prompt sent to the model looks like:

```
system: Every message has a UTC timestamp. The 'Now:' line below is the
        current time — use it for all time references. Never guess the time.

user: [Wed Jul 01, 02:36AM]  hello

system: Now: Wed Jul 01, 02:36 AM (night)
Session: just started · 1 message
```

## Stale-file detection (for coding agents)

The real killer feature: agents that read files and act on outdated content.

```python
from since.stale_files import stamp_file_read, check_and_invalidate

# Agent reads a file — stamp it
stamp_file_read("config.py", store, "session_1")

# File changes (git pull, manual edit, another tool)
# Next agent turn — stale warning fires automatically
if check_and_invalidate("config.py", store, "session_1"):
    # ⚠ Stale: "config.py" appears in the prompt tail
    # Agent sees the warning and re-reads
    stamp_file_read("config.py", store, "session_1")
```

The tail block shows:

```
⚠ Stale: "[FILE READ] config.py (mtime=...)" (read:config.py) — invalidated, 14m old
```

No daemon, no watcher, no polling — just mtime comparison at the next agent turn.

```python
from since.stale_files import stamp_file_read, check_and_invalidate
```

## How it works

| Component | What it does |
|---|---|
| `@since_time` | Decorator that wraps any chat function, timestamps every message, adds the clock |
| `with_time()` | Formats history with absolute timestamps + tail block (Now, gaps, stale warnings) |
| `Store` | SQLite backend — persistent, thread-safe, WAL mode, zero deps |
| `stamp_file_read()` | Records a file read as an event with current mtime |
| `check_and_invalidate()` | Compares stored vs current mtime, invalidates if changed |
| `timeparse` | Natural language time parser — "3 days ago", "yesterday at 3pm" |

## TTL classes

| Class | Decay | Use case |
|---|---|---|
| `permanent` | Never | User identity, project facts |
| `slow` | Session age | Normal conversation |
| `event` | `store.invalidate(source_id)` | File reads, tool outputs |
| `ephemeral` | 5 minutes | "ok", "thanks", throwaway messages |

## Requirements

- Python 3.10+
- No dependencies (zero — no numpy, no pydantic, no LLM SDK)

## Install

```bash
pip install pysince
```

Import as:

```python
from since import Store, since_time
from since.stale_files import stamp_file_read, check_and_invalidate
```
