# Build a bounded agent

This recipe follows the checked support-triage agent, which receives a bounded
policy lookup tool and one ticket-escalation effect. The `.one` excerpt mirrors
the current owning example; the focused Rust APIs are illustrative end-product
syntax.

## 1. Declare the agent contract

```one
one 1

semantic example.support
    domain one.agent@1
    agent SupportTriage@1
        instructions "Triage support requests using policy evidence before escalation."
        interface
            one shot example.support#Triage.Run@1
            start example.support#Triage.Start@1
            turn example.support#Triage.Turn@1
            resume example.support#Triage.Resume@1
        tool lookup_policy uses example.support#Policy.Lookup@1
        tool escalate_ticket uses example.support#Tickets.Escalate@1
        context request
        context completed turns
        context tool results
        model
            text true
            tool calling true
            streaming true
        budget
            input tokens 10000
            output tokens 1000
            cost microunits 10000
            elapsed 10s
            context bytes 65536
            tool calls 4
            child agents 0
            network bytes 0
            effects 1
        retention encrypted
        delegation maximum children 0
```

The prompt does not grant authority. The selected session fixes subject scope,
principal, tools, disclosure, budgets, provider binding, and delegation before
the model sees any context.

## 2. Keep tools typed

```rust,ignore
#[one::agent::tool(claims::LookupPolicy)]
async fn lookup_policy(ctx: ToolContext, input: TriageRequest)
    -> ToolResult<TriageResponse>
{
    ctx.operations().policy().lookup(input).await
}

#[one::agent::tool(claims::EscalateTicket)]
async fn escalate_ticket(
    ctx: ToolContext,
    request: TriageRequest,
) -> ToolResult<TriageResponse> {
    ctx.operations().tickets().escalate(request).await
}
```

The model calls versioned operations and receives typed results, progress,
faults, approval suspension, and evidence references. It never emits shell
text that bypasses the operation boundary.

## 3. Separate proposal from effect

```json
{
  "subject": "ticket/support-7391",
  "operation": "example.support#Tickets.Escalate@1",
  "reason": "policy-requires-human-review",
  "authority": "approval-required",
  "expires_at": "2027-01-15T12:00:00Z"
}
```

The approval view displays the exact operation, subject, amount, disclosed
data, provider, cost, expiry, and blast radius. Approval authorizes only that
exact request under a fresh grant; editing the amount produces a new proposal.

## 4. Suspend and resume exactly

```rust,ignore
match agent.run(case).await {
    AgentRun::Completed(answer) => publish(answer),
    AgentRun::AwaitingApproval(request) => suspend(request),
    AgentRun::BudgetExhausted(usage) => hand_off(usage),
    AgentRun::Cancelled(reason) => record_cancel(reason),
    AgentRun::OutcomeUnknown(effect) => reconcile(effect),
}
```

Resumption uses retained agent, tool, approval, and resource events. It does
not reconstruct authority from conversation text or repeat completed tools.

## 5. Select the model independently

```console
cargo test -p support-triage-agent --all-targets --offline
cargo run -p support-triage-agent --offline
```

Planning can select a local or hosted model only if its data handling,
retention, region, context limits, tool behavior, price, identity, and evidence
satisfy the root. Model quality informs selection but never grants a tool.

## Executable evidence

The checked-in
[support-triage project](https://github.com/muijf/one/tree/main/systems/system-idl/examples/support-triage-agent)
proves source/Rust parity, exact tool closure, a two-call tool trace, one
admitted effect, bounded events and usage, deterministic inference, durable
encrypted session selection, release, History facts, and idempotent resume.

See [agents and automation](/one/guides/agents-and-automation) and
[workflows and agents](/one/model/workflows-and-agents).
