From 38a82328d7c403dcdfd0b0e6de53f28e47f0e368 Mon Sep 17 00:00:00 2001 From: Abir Abbas Date: Mon, 2 Mar 2026 16:54:57 -0500 Subject: [PATCH] docs: add human-in-the-loop approval docs to SDK READMEs Add approval workflow documentation with code examples to all three SDK READMEs (Python, TypeScript, Go), covering the waiting state feature for pausing agent execution pending human review. Co-Authored-By: Claude Opus 4.6 --- sdk/go/README.md | 34 ++++++++++++++++++++++++++++++++++ sdk/python/README.md | 35 +++++++++++++++++++++++++++++++++++ sdk/typescript/README.md | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/sdk/go/README.md b/sdk/go/README.md index 53442f00..133b26c2 100644 --- a/sdk/go/README.md +++ b/sdk/go/README.md @@ -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 diff --git a/sdk/python/README.md b/sdk/python/README.md index 940605bf..9becf294 100644 --- a/sdk/python/README.md +++ b/sdk/python/README.md @@ -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 diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md index fb302e27..0585654a 100644 --- a/sdk/typescript/README.md +++ b/sdk/typescript/README.md @@ -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.