# The owner's AI and the Morning Review

Part of the [Ownbox SDK reference](https://www.ownbox.io/sdk/reference.md).

## m.tool

```python
m.tool(name, *, fn, description, args=None, title=None, render=None)
```

A question the owner's AI may ask the machine.

`fn(**args)` answers it, and `render(result)` writes the answer in words, which reach the owner's AI quoted, as the machine's own. `args` describes each argument: `{"type": "string" | "integer" | "boolean", "required", "description"}`, at most 10. The name is lowercase letters, digits and underscores.

**Needs in machine.yaml:** Nothing

Example (where the machine is defined):

```python
def open_quotes(limit=5):
    return {"quotes": ["Gutter repair", "Roof check"][:int(limit)]}

m.tool("open_quotes", fn=open_quotes, description="The quotes still waiting for an answer.",
       args={"limit": {"type": "integer", "required": False, "description": "How many, at most."}},
       render=lambda r: "\n".join(f"- {q}" for q in r["quotes"]) or "None waiting.")
```

## m.reporter

```python
m.reporter(title, fn)
```

The machine's part of the owner's Morning Review.

`fn(day)` answers for that day (a `datetime.date`). Your Ownbox asks once a pass and shows the latest answer, so a slow part never holds the review up. An empty answer shows nothing.

**Needs in machine.yaml:** Nothing

Example (where the machine is defined):

```python
def morning(day):
    sent = int(m.setting("sent_" + day.isoformat(), default=0) or 0)
    if not sent:
        return {}
    return {"title": "Quote Follow Up", "headline": {"value": sent, "label": "quotes followed up"}}

m.reporter("Quote Follow Up", morning)
```

## m.day_window

```python
m.day_window(day)
```

The UTC bounds of one of the Ownbox's local days.

In the Ownbox's own time zone, as its Morning Review counts a day.

**Needs in machine.yaml:** Nothing

**Returns:** `(start, end)`, as ISO 8601 text.

Example (inside a job):

```python
import datetime

start, end = m.day_window(datetime.date.today())
```

