Skip to content
Merged
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
34 changes: 34 additions & 0 deletions sdk/go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,40 @@ func main() {
- `types`: Shared data structures and contracts.
- `ai`: Helpers for interacting with AI providers via the control plane.

## Human-in-the-Loop Approvals

The `client` package provides methods for requesting human approval, checking status, and waiting for decisions:

```go
import "github.com/Agent-Field/agentfield/sdk/go/client"

approvalClient := client.New("http://localhost:8080", nil)

// Request approval — transitions execution to "waiting"
_, err := approvalClient.RequestApproval(ctx, nodeID, executionID,
client.RequestApprovalRequest{
Title: "Review Deployment",
ProjectID: "my-project",
TemplateType: "plan-review-v1",
ExpiresInHours: 24,
},
)

// Wait for human decision (uses context.Context for timeout)
waitCtx, cancel := context.WithTimeout(ctx, 1*time.Hour)
defer cancel()

result, err := approvalClient.WaitForApproval(waitCtx, nodeID, executionID,
&client.WaitForApprovalOptions{
PollInterval: 5 * time.Second,
MaxInterval: 30 * time.Second,
},
)
// result.Status is "approved", "rejected", or "expired"
```

**Methods:** `RequestApproval()`, `GetApprovalStatus()`, `WaitForApproval()`

## Testing

```bash
Expand Down
35 changes: 35 additions & 0 deletions sdk/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,41 @@ if __name__ == "__main__":
agent.serve(port=8001)
```

## Human-in-the-Loop Approvals

The Python SDK provides a first-class waiting state for pausing agent execution mid-reasoner and waiting for human approval:

```python
from agentfield import Agent, ApprovalResult

app = Agent(node_id="reviewer", agentfield_server="http://localhost:8080")

@app.reasoner()
async def deploy(environment: str) -> dict:
plan = await app.ai(f"Create deployment plan for {environment}")

# Pause execution and wait for human approval
result: ApprovalResult = await app.pause(
approval_request_id="req-abc123",
expires_in_hours=24,
timeout=3600,
)

if result.approved:
return {"status": "deploying", "plan": str(plan)}
elif result.changes_requested:
return {"status": "revising", "feedback": result.feedback}
else:
return {"status": result.decision}
```

**Two API levels:**

- **High-level:** `app.pause()` blocks the reasoner until approval resolves, with automatic webhook registration
- **Low-level:** `client.request_approval()`, `client.get_approval_status()`, `client.wait_for_approval()` for fine-grained control

See `examples/python_agent_nodes/waiting_state/` for a complete working example.

See `docs/DEVELOPMENT.md` for instructions on wiring agents to the control plane.

## Testing
Expand Down
38 changes: 38 additions & 0 deletions sdk/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,41 @@ agent.reasoner('process', async (ctx) => {
```

**Use `note()` for AgentField UI tracking, `console.log()` for local debugging.**

## Human-in-the-Loop Approvals

Use the `ApprovalClient` to pause agent execution for human review:

```ts
import { Agent, ApprovalClient } from '@agentfield/sdk';

const agent = new Agent({ nodeId: 'reviewer', agentFieldUrl: 'http://localhost:8080' });
const approvalClient = new ApprovalClient({
baseURL: 'http://localhost:8080',
nodeId: 'reviewer',
});

agent.reasoner<{ task: string }, { status: string }>('deploy', async (ctx) => {
const plan = await ctx.ai(`Create deployment plan for: ${ctx.input.task}`);

// Request approval — transitions execution to "waiting"
await approvalClient.requestApproval(ctx.executionId, {
projectId: 'my-project',
title: `Deploy: ${ctx.input.task}`,
description: String(plan),
expiresInHours: 24,
});

// Wait for human decision (polls with exponential backoff)
const result = await approvalClient.waitForApproval(ctx.executionId, {
pollIntervalMs: 5_000,
timeoutMs: 3_600_000,
});

return { status: result.status };
});
```

**Methods:** `requestApproval()`, `getApprovalStatus()`, `waitForApproval()`

See `examples/ts-node-examples/waiting-state/` for a complete working example.
Loading