diff --git a/README.md b/README.md index 2112d43..a644ca1 100644 --- a/README.md +++ b/README.md @@ -298,8 +298,16 @@ const handle = onecli.configureManualApproval(async (request) => { console.log(`${request.method} ${request.url}`); console.log(`Agent: ${request.agent.name}`); - if (request.bodyPreview) { - console.log(`Body: ${request.bodyPreview}`); + // `summary` is a structured, human-readable description of the request + // (e.g. a Gmail send's base64 body decoded into To/Subject/Body). + // `bodyPreview` is the same content flattened to text — safe to display directly. + if (request.summary) { + console.log(request.summary.action); // e.g. "Send email" + for (const { label, value } of request.summary.details) { + console.log(`${label}: ${value}`); // e.g. "To: a@b.com" + } + } else if (request.bodyPreview) { + console.log(request.bodyPreview); } // Return 'approve' to forward the request, 'deny' to block it @@ -322,12 +330,22 @@ The callback is called once per pending approval. Multiple approvals are handled | `host` | `string` | Hostname | | `path` | `string` | Request path | | `headers` | `Record` | Sanitized request headers (no credentials) | -| `bodyPreview` | `string \| null` | First 4KB of the request body, or `null` | +| `bodyPreview` | `string \| null` | Human-readable text rendering of the request, safe to display | +| `summary` | `ApprovalSummary \| null` (optional) | Structured form of `bodyPreview` (see below); may be absent on older gateways | | `agent` | `{ id: string; name: string; externalId: string \| null }` | The agent that made the request | | `createdAt` | `string` | When the request arrived (ISO 8601) | | `expiresAt` | `string` | When the approval expires (ISO 8601) | | `timeoutSeconds` | `number` | Seconds until auto-deny (300) | +Where `ApprovalSummary` is: + +```typescript +interface ApprovalSummary { + action: string; // e.g. "Send email" + details: { label: string; value: string }[]; // e.g. [{ label: "To", value: "a@b.com" }] +} +``` + **Returns** `ManualApprovalHandle` with a `stop()` method to disconnect. --- diff --git a/src/approvals/types.ts b/src/approvals/types.ts index a876495..08e84fc 100644 --- a/src/approvals/types.ts +++ b/src/approvals/types.ts @@ -1,3 +1,25 @@ +/** One key fact about a held request, e.g. `{ label: "To", value: "a@b.com" }`. */ +export interface ApprovalDetail { + /** Field name shown to the approver (e.g. "To", "Subject"). */ + label: string; + /** Field value shown to the approver. */ + value: string; +} + +/** + * Structured, human-readable description of what a held request will do. + * + * The gateway decodes this from the (often opaque) request body — e.g. a Gmail + * send's base64 MIME becomes `{ action: "Send email", details: [...] }` — so it + * can be rendered as fields/rows instead of raw bytes. + */ +export interface ApprovalSummary { + /** Short action title, e.g. "Send email" or "Delete calendar event". */ + action: string; + /** Ordered key facts about the request. */ + details: ApprovalDetail[]; +} + /** A single request awaiting manual approval. */ export interface ApprovalRequest { /** Unique approval ID. */ @@ -12,8 +34,18 @@ export interface ApprovalRequest { path: string; /** Sanitized request headers (no auth headers). */ headers: Record; - /** First ~4KB of request body as text, or null if no body. */ + /** + * Human-readable, length-bounded rendering of the request as plain text — + * safe to display directly (never raw base64/binary). Mirrors `summary` + * flattened to text; `null` only when there is nothing to summarize. + */ bodyPreview: string | null; + /** + * Structured form of `bodyPreview` for richer rendering (render + * `summary.details` as fields/rows). Absent on older gateways, or `null` + * when no summary is available — fall back to `bodyPreview`. + */ + summary?: ApprovalSummary | null; /** The agent that made this request. */ agent: { id: string; name: string; externalId: string | null }; /** When the request arrived (ISO 8601). */ diff --git a/src/index.ts b/src/index.ts index cb031b9..93a14c9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,8 @@ export type { } from "./agents/types.js"; export type { ApprovalRequest, + ApprovalSummary, + ApprovalDetail, ManualApprovalCallback, ManualApprovalHandle, } from "./approvals/types.js";