# Job Profiles Design Document
## [ROUTING] Streamline local Timmy automation context per job

**Issue:** timmy-config #90  
**Author:** Timmy (AI Agent)  
**Date:** 2026-03-30  
**Status:** Design Complete - Ready for Implementation

---

## Executive Summary

Local Hermes sessions experience context thrashing when all 40 tools (~9,261 tokens of schema) are loaded for every job. This design introduces **job-specific toolset profiles** that narrow the tool surface based on task type, achieving **73-91% token reduction** and preventing the "loop or thrash" behavior observed in long-running automation.

---

## Problem Statement

When `toolsets: [all]` is enabled (current default in `~/.hermes/config.yaml`), every AIAgent instantiation loads:

- **40 tools** across 12+ toolsets
- **~9,261 tokens** of JSON schema
- Full browser automation (12 tools)
- Vision, image generation, TTS, MoA reasoning
- All MCP servers (Morrowind, etc.)

For a simple cron job checking Gitea issues, this is massive overkill. The LLM:
1. Sees too many options
2. Hallucinates tool calls that aren't needed
3. Gets confused about which tool to use
4. Loops trying different approaches

---

## Solution Overview

Leverage the existing `enabled_toolsets` parameter in `AIAgent.__init__()` to create **job profiles**—pre-defined toolset combinations optimized for specific automation types.

### Key Design Decisions

| Decision | Rationale |
|----------|-----------|
| Use YAML profiles, not code | Easy to extend without deployment |
| Map to existing toolsets | No changes needed to Hermes core |
| 5 base profiles | Covers 95% of automation needs |
| Token estimates in comments | Helps users understand trade-offs |

---

## Profile Specifications

### 1. CODE-WORK Profile
**Purpose:** Software development, git operations, code review

```yaml
toolsets: [terminal, file]
tools_enabled: 6
token_estimate: "~2,194 tokens"
token_savings: "~76%"
```

**Included Tools:**
- `terminal`, `process` - git, builds, shell commands
- `read_file`, `search_files`, `write_file`, `patch`

**Use Cases:**
- Automated code review
- Refactoring tasks
- Build and test automation
- Git branch management

**Not Included:**
- Web search (assumes local docs/code)
- Browser automation
- Vision/image generation

---

### 2. RESEARCH Profile
**Purpose:** Information gathering, documentation lookup, analysis

```yaml
toolsets: [web, browser, file]
tools_enabled: 15
token_estimate: "~2,518 tokens"
token_savings: "~73%"
```

**Included Tools:**
- `web_search`, `web_extract` - quick lookups
- Full browser suite (12 tools) - deep research
- File tools - save findings, read local docs

**Use Cases:**
- API documentation research
- Competitive analysis
- Fact-checking reports
- Technical due diligence

**Not Included:**
- Terminal (prevents accidental local changes)
- Vision/image generation

---

### 3. TRIAGE Profile
**Purpose:** Read-only status checking, issue monitoring, health checks

```yaml
toolsets: [terminal, file]
tools_enabled: 6
token_estimate: "~2,194 tokens"
token_savings: "~76%"
read_only: true  # enforced via prompt
```

**Included Tools:**
- `terminal` - curl for Gitea API, status commands
- `read_file`, `search_files` - log analysis, config inspection

**Critical Note on Write Safety:**
The `file` toolset includes `write_file` and `patch`. For truly read-only triage, the job prompt **MUST** include:

```
[SYSTEM: This is a READ-ONLY triage job. Only use read_file and search_files.
Do NOT use write_file, patch, or terminal commands that modify state.]
```

**Future Enhancement:**
Consider adding a `disabled_tools` parameter to AIAgent for granular control without creating new toolsets.

**Use Cases:**
- Gitea issue triage
- CI/CD status monitoring
- Log file analysis
- System health checks

---

### 4. CREATIVE Profile
**Purpose:** Content creation, writing, editing

```yaml
toolsets: [file, web]
tools_enabled: 4
token_estimate: "~1,185 tokens"
token_savings: "~87%"
```

**Included Tools:**
- `read_file`, `search_files`, `write_file`, `patch`
- `web_search`, `web_extract` - references, fact-checking

**Use Cases:**
- Documentation writing
- Content generation
- Editing and proofreading
- Newsletter/article composition

**Not Included:**
- Terminal (no system access needed)
- Browser (web_extract sufficient for text)
- Vision/image generation

---

### 5. OPS Profile
**Purpose:** System operations, maintenance, deployment

```yaml
toolsets: [terminal, process, file]
tools_enabled: 6
token_estimate: "~2,194 tokens"
token_savings: "~76%"
```

**Included Tools:**
- `terminal`, `process` - service management, background tasks
- File tools - config editing, log inspection

**Use Cases:**
- Server maintenance
- Log rotation
- Service restart
- Deployment automation
- Docker container management

---

## How Toolset Filtering Works

The Hermes harness already supports this via `AIAgent.__init__`:

```python
def __init__(
    self,
    ...
    enabled_toolsets: List[str] = None,   # Only these toolsets
    disabled_toolsets: List[str] = None,  # Exclude these toolsets
    ...
):
```

The filtering happens in `model_tools.get_tool_definitions()`:

```python
def get_tool_definitions(
    enabled_toolsets: List[str] = None,
    disabled_toolsets: List[str] = None,
    ...
) -> List[Dict[str, Any]]:
    # 1. Resolve toolsets to tool names via toolsets.resolve_toolset()
    # 2. Filter by availability (check_fn for each tool)
    # 3. Return OpenAI-format tool definitions
```

### Current Cron Usage (Line 423-443 in `cron/scheduler.py`):

```python
agent = AIAgent(
    model=turn_route["model"],
    ...
    disabled_toolsets=["cronjob", "messaging", "clarify"],  # Hardcoded
    quiet_mode=True,
    platform="cron",
    ...
)
```

---

## Wiring into Cron Dispatch

### Step 1: Load Profile

```python
import yaml
from pathlib import Path

def load_job_profile(profile_name: str) -> dict:
    """Load a job profile from ~/.timmy/uniwizard/job_profiles.yaml"""
    profile_path = Path.home() / ".timmy/uniwizard/job_profiles.yaml"
    with open(profile_path) as f:
        config = yaml.safe_load(f)
    profiles = config.get("profiles", {})
    return profiles.get(profile_name, profiles.get("minimal", {"toolsets": ["file"]}))
```

### Step 2: Modify `run_job()` in `cron/scheduler.py`

```python
def run_job(job: dict) -> tuple[bool, str, str, Optional[str]]:
    ...
    # Load job profile (default to minimal if not specified)
    profile_name = job.get("tool_profile", "minimal")
    profile = load_job_profile(profile_name)
    
    # Build toolset filter
    enabled_toolsets = profile.get("toolsets", ["file"])
    disabled_toolsets = profile.get("disabled_toolsets", ["cronjob", "messaging", "clarify"])
    
    agent = AIAgent(
        model=turn_route["model"],
        ...
        enabled_toolsets=enabled_toolsets,      # NEW
        disabled_toolsets=disabled_toolsets,    # MODIFIED
        quiet_mode=True,
        platform="cron",
        ...
    )
```

### Step 3: Update Job Definition Format

Add `tool_profile` field to `~/.hermes/cron/jobs.yaml`:

```yaml
jobs:
  - id: daily-issue-triage
    name: "Triage Gitea Issues"
    schedule: "0 9 * * *"
    tool_profile: triage    # <-- NEW
    prompt: "Check timmy-config repo for new issues..."
    deliver: telegram

  - id: weekly-docs-review
    name: "Review Documentation"
    schedule: "0 10 * * 1"
    tool_profile: creative  # <-- NEW
    prompt: "Review and improve README files..."
```

---

## Token Savings Summary

| Profile | Tools | Tokens | Savings |
|---------|-------|--------|---------|
| Full (`all`) | 40 | ~9,261 | 0% |
| code-work | 6 | ~2,194 | -76% |
| research | 15 | ~2,518 | -73% |
| triage | 6 | ~2,194 | -76% |
| creative | 4 | ~1,185 | -87% |
| ops | 6 | ~2,194 | -76% |
| minimal | 4 | ~800 | -91% |

**Benefits:**
1. Faster prompt processing (less context to scan)
2. Reduced API costs (fewer input tokens)
3. More focused tool selection (less confusion)
4. Faster tool calls (smaller schema to parse)

---

## Migration Path

### Phase 1: Deploy Profiles (This PR)
- [x] Create `~/.timmy/uniwizard/job_profiles.yaml`
- [x] Create design document
- [ ] Post Gitea issue comment

### Phase 2: Cron Integration (Next PR)
- [ ] Modify `cron/scheduler.py` to load profiles
- [ ] Add `tool_profile` field to job schema
- [ ] Update existing jobs to use appropriate profiles

### Phase 3: CLI Integration (Future)
- [ ] Add `/profile` slash command to switch profiles
- [ ] Show active profile in CLI banner
- [ ] Profile-specific skills loading

---

## Files Changed

| File | Purpose |
|------|---------|
| `~/.timmy/uniwizard/job_profiles.yaml` | Profile definitions |
| `~/.timmy/uniwizard/job_profiles_design.md` | This design document |

---

## Open Questions

1. **Should we add `disabled_tools` parameter to AIAgent?**
   - Would enable true read-only triage without prompt hacks
   - Requires changes to `model_tools.py` and `run_agent.py`

2. **Should profiles include model recommendations?**
   - e.g., `recommended_model: claude-opus-4` for code-work
   - Could help route simple jobs to cheaper models

3. **Should we support profile composition?**
   - e.g., `profiles: [ops, web]` for ops jobs that need web lookup

---

## References

- Hermes toolset system: `~/.hermes/hermes-agent/toolsets.py`
- Tool filtering logic: `~/.hermes/hermes-agent/model_tools.py:get_tool_definitions()`
- Cron scheduler: `~/.hermes/hermes-agent/cron/scheduler.py:run_job()`
- AIAgent initialization: `~/.hermes/hermes-agent/run_agent.py:AIAgent.__init__()`
