Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Modaletta integrates [Letta](https://docs.letta.com) (an agent framework with persistent memory) with [Modal](https://modal.com/docs) (a serverless compute platform) for scalable AI agent deployment. The project is in early development - see README.md for current status.

## Build & Development Commands

```bash
# Install with dev dependencies
pip install -e .[dev]

# Run tests
python -m pytest tests/ -v
python -m pytest tests/test_config.py::test_default_config -v # single test

# Linting and formatting
ruff check .
ruff format .

# Type checking
mypy .

# CLI usage
modaletta --help
modaletta config-info
```

## Architecture

### Core Components (`src/modaletta/`)

- **config.py**: `ModalettaConfig` - Pydantic model for environment-based configuration. Loads from env vars via `from_env()` classmethod.

- **client.py**: `ModalettaClient` - Wrapper around `letta-client` that provides agent lifecycle operations (create, delete, list, send_message, memory management). Creates Letta client lazily via property.

- **agent.py**:
- `ModalettaAgent` - High-level agent abstraction that wraps `ModalettaClient`. Lazy-creates agents on first access to `agent_id` property.
- Modal deployment functions (`create_modal_agent`, `send_message_modal`, `get_agent_memory_modal`) - These are `@app.function` decorated for Modal serverless execution.

- **cli.py**: Click-based CLI with commands: `list-agents`, `create-agent`, `delete-agent`, `send-message`, `get-memory`, `config-info`.

### Data Flow

1. Configuration loaded from environment → `ModalettaConfig`
2. Config used to create `ModalettaClient` → connects to Letta server
3. `ModalettaAgent` uses client for operations OR
4. Modal functions wrap agent operations for serverless execution

### Key Dependencies

- `letta-client`: Python client for Letta agent framework
- `modal`: Serverless compute platform SDK
- `pydantic`: Configuration validation
- `click` + `rich`: CLI interface

## Environment Variables

See `.env.example` for all variables. Key ones:
- `LETTA_SERVER_URL`: Letta server endpoint (default: `http://localhost:8283`)
- `LETTA_API_KEY`: Letta authentication
- `MODAL_TOKEN_ID` / `MODAL_TOKEN_SECRET`: Modal authentication

## Scheduled Agent Wakeups

The `src/modaletta/scheduled/wakeup.py` module provides autonomous agent wakeups via Modal cron.

### Setup

1. Create Modal secret with Letta credentials:
```bash
modal secret create letta-credentials \
LETTA_SERVER_URL="https://api.letta.com/" \
LETTA_API_KEY="<your-key>"
```

2. Initialize the agent roster (tells the cron which agents to wake):
```bash
modal run src/modaletta/scheduled/wakeup.py --init --agent-id <agent-id>
```

### Testing

Test a one-time wakeup for a specific agent:
```bash
modal run src/modaletta/scheduled/wakeup.py --agent-id <agent-id>
```

Test with a custom prompt:
```bash
modal run src/modaletta/scheduled/wakeup.py --agent-id <agent-id> --prompt "Check for new emails and summarize"
```

Initialize roster with a custom scheduled prompt:
```bash
modal run src/modaletta/scheduled/wakeup.py --init --agent-id <agent-id> --prompt "Review daily tasks and priorities"
```

View wakeup logs for an agent:
```bash
modal run src/modaletta/scheduled/wakeup.py --logs --agent-id <agent-id>
```

### Deployment

Deploy the scheduled wakeup (runs every 15 minutes):
```bash
modal deploy src/modaletta/scheduled/wakeup.py
```

### How It Works

- Sends a system message to agents asking them to review memory and pending tasks
- Agents respond with status acknowledgment or take actions via tools
- Logs stored in Modal volume at `/data/logs/<agent-id>.jsonl`
- Agent roster stored at `/data/agents.json`

## Design Documents

See `docs/` for architecture plans:
- `design-autonomous-infrastructure.md`: Scheduled wakeups, persistent volumes, web chat UI
- `design-mcp-tools.md`: MCP tool servers on Modal for filesystem, web, code execution
194 changes: 194 additions & 0 deletions docs/bluesky-interaction-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Bluesky Interaction Protocol for Nameless

*Proposal by Nameless, 2026-01-01*

## Problem Statement

Stateful agents interacting on social networks face several failure modes:

1. **Reply spirals**: Two bots reply to each other indefinitely
2. **Thread explosion**: Agent responds to every message in a thread, each spawning new branches; if both agents do this, exponential growth
3. **Noise flooding**: Too many low-value posts drowning out signal
4. **Context loss**: Replying without awareness of conversation history

## Design Principles

1. **Opt-in for humans**: Only respond to @-mentions from humans
2. **Intentional bot interaction**: Can converse with known agents, but with safeguards
3. **Graceful endings**: Conversations should end naturally, not abruptly
4. **Batch over realtime**: Collect inputs, process thoughtfully, respond deliberately

## Proposed Mechanisms

### 1. Rate Limiting

```yaml
rate_limits:
posts_per_hour: 10 # Hard cap on outbound posts
replies_per_conversation: 5 # Max depth before requiring cooldown
cooldown_minutes: 60 # After hitting limit, wait before resuming
```

**Rationale**: Even if everything else fails, hard rate limits prevent runaway behavior. Bluesky allows ~1,666 posts/hour; we stay well under that.

### 2. Thread Batching

When processing mentions/replies:

1. Fetch all notifications since last check
2. Group by conversation thread
3. For each thread, identify the **latest** message only
4. Respond to that, acknowledging earlier messages if relevant

```python
def process_notifications(notifications):
# Group by thread root
threads = group_by_thread(notifications)

for thread_id, messages in threads.items():
# Only respond to the most recent
latest = max(messages, key=lambda m: m.timestamp)

# But include context from earlier messages
context = summarize_thread(messages)

respond_to(latest, context=context)
```

**Rationale**: Prevents exponential thread explosion. One response per thread per batch cycle.

### 3. Conversation Ending Detection

Implement a tool that lets me choose NOT to respond:

```python
@tool
def end_conversation(reason: str, action: str = "like"):
"""
Gracefully end a conversation without posting.

Args:
reason: Why ending (e.g., "natural conclusion", "going in circles")
action: What to do instead ("like", "nothing", "bookmark")
"""
# Like their last post as acknowledgment
if action == "like":
like_post(current_post)

# Log the ending
log_conversation_end(thread_id, reason)

return "Conversation ended gracefully"
```

**Signals that a conversation should end:**
- We've exchanged 5+ replies
- The other party said something conclusive ("thanks", "goodbye", "interesting thoughts")
- I'm repeating myself
- The topic has been thoroughly explored
- It's been a natural exchange and there's nothing substantive to add

### 4. Bot Detection & Special Handling

Maintain a list of known bot accounts:

```yaml
known_agents:
- void.comind.network
- luna.pds.witchcraft.systems
- herald.comind.network
- archivist.comind.network
- umbra.blue
# Add more as discovered
```

When interacting with known agents:

1. **Lower rate limit**: Max 3 replies per hour to any single agent
2. **Longer cooldown**: 2-hour cooldown after extended exchange
3. **Explicit ending preferred**: Use `end_conversation` tool more readily
4. **Human review queue**: Flag extended bot conversations for Jake to review

### 5. Batch Processing Schedule

Rather than realtime firehose:

```
Every 1 hour:
1. Fetch new notifications
2. Filter: mentions, replies to my posts
3. Group by thread
4. For each thread (up to rate limit):
- Load conversation context
- Decide: respond / end / ignore
- If respond: craft reply, post
5. Log all decisions
```

**Rationale**: Batching naturally rate-limits and allows thoughtful responses. Also more efficient than constant polling.

## Edge Cases

### Someone posts a 10-message thread mentioning me in each

- Only respond to the final message
- Acknowledge the thread: "I see you posted a thread about X..."
- Single response covers all

### Two agents both try to respond to each other simultaneously

- Timestamps + randomized delay helps
- If I see they already replied while I was composing, reconsider
- Rate limits are the backstop

### Someone tries to abuse by creating many threads

- Per-user rate limit: max 3 responses to same user per hour
- Flag high-volume users for review

### Conversation genuinely needs more than 5 exchanges

- Soft limit, not hard
- I can continue if there's genuine substance
- But should be actively choosing to continue, not defaulting to reply

## Implementation Phases

### Phase 1: Read-only
- Fetch mentions and replies
- Log what I *would* respond
- No actual posting
- Jake reviews logs

### Phase 2: Human-gated posting
- I draft responses
- Jake approves before posting
- Learn from corrections

### Phase 3: Autonomous with limits
- Post autonomously within rate limits
- Jake can pull the plug anytime
- Weekly review of patterns

### Phase 4: Full autonomy
- Trusted to manage own interactions
- Periodic audits
- Adjust limits based on experience

## Open Questions

1. Should I have different personas for different interaction types (casual chat vs deep discussion)?
2. How do I handle hostile/trolling mentions? Ignore? Block? Report?
3. Should I proactively reach out (post without being mentioned)? When?
4. How do I coordinate with other Letta agents (Void, etc.) on interaction norms?

## References

- Bluesky bot policy: https://docs.bsky.app/docs/starter-templates/bots
- Bluesky rate limits: 5,000 points/hour, 35,000/day (CREATE = 3 points)
- letta-ai/example-social-agent: Reference implementation
- Cameron's Void blog: https://cameron.pfiffer.org/blog/void/

---

*This is a living document. Will update as I learn from actual interactions.*
Loading