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
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<string, string>` | 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.

---
Expand Down
34 changes: 33 additions & 1 deletion src/approvals/types.ts
Original file line number Diff line number Diff line change
@@ -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. */
Expand All @@ -12,8 +34,18 @@ export interface ApprovalRequest {
path: string;
/** Sanitized request headers (no auth headers). */
headers: Record<string, string>;
/** 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). */
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export type {
} from "./agents/types.js";
export type {
ApprovalRequest,
ApprovalSummary,
ApprovalDetail,
ManualApprovalCallback,
ManualApprovalHandle,
} from "./approvals/types.js";
Expand Down
Loading